 MSDN Magazine May 2003

MSDN Magazine May 2003

Real-World XML: Manipulate XML Data Easily with Integrated Readers and Writers in the .NET Framework

In the .NET Framework, XmlTextReader and XmlTextWriter provide for XML-driven reading and writing operations. In this article, the author discusses the architecture of readers and how they relate to XMLDOM and SAX parsers. He also shows how to use readers to parse and validate XML documents, how to leverage writers to create well-formed documents, and how to optimize the processing of large XML documents using functions to read and write Base64 and BinHex-encoded text. He then reviews how to implement a stream-based read/write parser that combines the functions of a reader and a writer into a single class.

Dino Esposito Debugging Tool: Build a Logging and Event Viewing Library to Help Debug Your .NET Framework-based App

Building a basic, reusable application framework can make development quicker and easier. This allows you to focus more on the problems at hand and less on the repetitive tasks involved in building any application. In this article, the author presents a framework that provides facilities to access the registry and an extensible framework for logging messages to a console window or the Event Viewer. This reusable framework can be included as a library in your projects, allowing you to display an enhanced, color-coded message log and dynamically change logging levels.

Daryn Kiely Metadata: Create a Database Schema Repository with Meta Data Services in SQL Server 2000

SQL Server 2000 Meta Data Services is a repository technology that stores and manages metadata for SQL Server. Instead of building database schemas over and over, Meta Data Services allows you to freeze an entire schema for use in other projects. You can also use these schemas for training, testing, or debugging. In this article, the authors will review the various components of Meta Data Services and show how it can be programmed using a Visual Basic client, XML, and XSLT. They will also show you how to manage and manipulate your metadata by generating a simple database schema using a SQL Server repository.

Alok Mehta and Ricardo Rodriguez Visual Studio .NET: Building Windows Forms Controls and Components with Rich Design-Time Features, Part 2

This is the second of two articles discussing the extremely rich design-time features of the .NET Framework. Part 1 discussed the basics, showing you where to start and how to extend your control implementation through attributes and interfaces, as well as their effects on the property browser, code serialization, and other controls. Part 2 continues the journey by concentrating on design-time functionality that you can implement beyond your components and controls, including TypeConverters, UITypeEditors, and Designers. It would be impossible to cover everything you can do in two short articles, which is a testament to just how all-encompassing and flexible the design-time capability of the .NET Framework is.

Michael Weinhardt and Chris Sells Virus Hunting: Understand Common Virus Attacks Before They Strike to Better Protect Your Apps

Developer's machines can often be more vulnerable to viruses than the average corporate user because of their more frequent access to remote machines and shares, and the differing administrative privileges they maintain across mutiple machines. Reliance on antivirus software is fine as a first line of defense, but you need a basic arsenal of skills for securing the executables on your system and coping with viruses on your own. This article reviews proactive methods you can use to defend yourself against malicious executable code in resources, component libraries, scripts and macros, as well as how to avoid a handful of other potential vulnerabilities.

Jason Fisher Editor's Note: Get Ready For Microsoft Office 2003

Over the years, we've celebrated a lot of anniversaries in this column. Who can forget our "Ten Years of ENIAC!" Editor's Note back in June 1957? Or the "How Far We've Come: Five Years of the Altair 8800" issue back in January 1980? Or even the "Macintosh Interface: 15 Years Without an Update" Editor's Note page just four years ago?.

New Stuff: Resources for Your Developer Toolbox

Consonica Ltd. has announced StateStitch, which enables ASP. NET technology to be used with existing ASP applications. ASP. NET offers a more powerful feature set, but since ASP and ASP. NET applications cannot share session data, existing applications would otherwise need to be completely rewritten.

Theresa W. Carey Web Q&A: Storing SQL Data, URL Query Length, and More

Find out the best way to store large amounts of XML data in SQL Server, along with the performance implications. What's the maximum length of an XML query to SQL Server in a URL?

Edited by Nancy Michell Data Points: Techniques for Managing Rowset Paging

There are a number of ways to handle paging in Web applications. This month I'll examine several paging techniques and weigh their pros and cons. John Papa discusses how to manage paging and caching issues through the lower tiers of an n-tiered architecture, including how to make SQL Server manage the paging on your app's behalf.

John Papa Cutting Edge: Form-based Programming in ASP.NET

One of the most common snags that ASP developers encounter when they first approach ASP.NET is that managed Web applications must be written according to a single-form interface model. Find out how it works.

Dino Esposito The XML Files: Web Services Encoding and More

Aaron Skonnard covers the difference between document/literal and rpc/encoded Web Services and the history behind them.

Aaron Skonnard Advanced Basics: Windows Forms Controls

Build a control that allows the user to drag and drop other controls onto the new control at run time, and allow the user to move the control around on a form, all in Visual Basic.

Ken Spencer House of Web Services: Mandatory Headers in ASP.NET Web Services

The ASP.NET Web Services infrastructure includes support for programming with SOAP message headers. Unfortunately, the model for handling mandatory headers is flawed in that you need to write additional code so that a Web Service will not execute when a mandatory header is not processed. This column explores a specific problem that arises when you deal with mandatory SOAP handles and presents three solutions.

Tim Ewald Basic Instincts: Programming Events of the Framework Class Libraries

Some practical examples of handling some of the more commonly used events in the Microsoft .NET Framework.

Ted Pattison C++ Q&A: Window Destruction in the .NET Framework

If a window is destroyed, does the .NET Framework immediately destroy the corresponding Form, or does it wait until a garbage collection to do this? How can you release my resources as soon as the window is destroyed? This month Paul DiLascia gives you the answers.

Paul DiLascia Resource File: Windows Media 9 Series Digital Rights Management

If you have an application that handles Windows Media content and you need an effective way to track content usage, Windows Media 9 Series now offers Digital Rights Management (DRM). It allows you to take advantage of the peer-to-peer distribution model and still redirect users back to your app once they have downloaded your content (prior to viewing).


Real-World XML: Manipulate XML Data Easily with Integrated Readers and Writers in the .NET Framework

Real-World XML

Manipulate XML Data Easily with Integrated Readers and Writers in the .NET Framework

Dino Esposito
This article assumes you're familiar with XML and the .NET Framework
Level of Difficulty 1 2 3
Code download available at: Real-WorldXML.exe (120KB)
SUMMARY

In the .NET Framework, XmlTextReader and XmlTextWriter provide for XML-driven reading and writing operations. In this article, the author discusses the architecture of readers and how they relate to XMLDOM and SAX parsers. He also shows how to use readers to parse and validate XML documents, how to leverage writers to create well-formed documents, and how to optimize the processing of large XML documents using functions to read and write Base64 and BinHex-encoded text. He then reviews how to implement a stream-based read/write parser that combines the functions of a reader and a writer into a single class.

Contents
From MSXML to XML in .NET
The XML Parsing Model
The XmlReader Class
Parsing the Contents of Attributes
Manipulating XML Text
Strings and Fragments
Validating Readers
Node Readers
The XmlTextWriter Class
Reading and Writing Streams
Devising an XmlReadWriter Class
Summing it Up

A
bout three years ago, I left a software conference believing that no future programming would be possible without a strong understanding of XML. XML has indeed come a long way since the early days, finding its way into even the deepest recesses of common programming frameworks. In this article, I'll review the role and internal characteristics of the Microsoft® .NET Framework API that deals with XML documents and then I'll move on to address a few open points.

From MSXML to XML in .NET

Prior to the advent of the .NET Framework, you used to write XML-driven applications for Windows® by exploiting the services of MSXML—a COM-based library. Unlike the classes in the .NET Framework, the MSXML library is more of a bolted-on API than a piece of code that is fully integrated with the underlying operating system. MSXML can certainly communicate with the rest of your application, but it doesn't really integrate with the surrounding environment.

The MSXML library can be imported in Win32® and even common language runtime (CLR)-targeted code, but it remains an external black box acting as a server component. .NET Framework-based applications, on the other hand, can use XML core classes right alongside the other namespaces of the .NET Framework so that the final code is well integrated and easy to read.

As a self-contained component, the MSXML parser provides advanced features such as asynchronous parsing. This feature is apparently lacking in the XML classes of the .NET Framework. By integrating XML classes with other classes in the .NET Framework, however, you can easily obtain the same functions and gain even more control over the process.

A library of XML functions should provide at least a set of basic services including parsing, querying, and transformations. In the .NET Framework, you find classes that support XPath queries and XSLT transformations, as well as classes to read and write XML documents. In addition, the .NET Framework incorporates classes that perform XML-related tasks such as object serialization (the XmlSerializer and the SoapFormatter classes), application configuration (the AppSettingsReader class), and data persistence (the DataSet class). In this article, I'll focus on the classes that accomplish basic I/O operations.

The XML Parsing Model

Since XML is a markup language, a tool capable of analyzing and understanding the lexical syntax is needed to effectively use the information stored in the documents. This tool is the XML parser—a sort of black box component that reads in markup text and returns platform-specific objects.

All the XML parsers available to programmers fall in one of two main categories, regardless of the underlying platform: tree-based and event-based processors. These two categories are commonly identified with their two most popular and concrete implementations, the Microsoft XML Document Object Model (XMLDOM) and Simple API for XML (SAX). The XMLDOM parser is a generic tree-based API which renders XML documents as an in-memory structure. The SAX parser provides an event-based API for processing each significant element in a stream of XML data. In general, a Document Object Model (DOM) implementation can be loaded from a SAX stream, so the two types of processors are not mutually exclusive.

Conceptually speaking, a SAX parser is diametrically opposed to an XMLDOM parser and the gap between the two models is indeed fairly large. XMLDOM is well defined in its set of functionalities and there is not much more you can reasonably expect from the evolution of this model. With this large set of functionalities, of course, comes a downside—a large memory footprint and hefty bandwidth that is required in order to process large documents.

SAX parsers work by letting client applications pass living instances of platform-specific objects to handle parser events. The parser controls the whole process and pushes data to the application which, in turn, is free to accept or just ignore it. This model is extremely lean and features a very limited memory footprint.

The .NET Framework provides full support for the XMLDOM parsing model, but not for SAX. There's a good reason for this. The .NET Framework supports two different models of parser: XMLDOM parsers and XML readers. The apparent lack of support for SAX parsers does not mean that you have to renounce the functionality that they offer. All the functions of a SAX parser can be implemented easily and more effectively by using an XML reader. Unlike a SAX parser, a .NET Framework reader works under the total control of the client application. In this way, the application itself can then pull out only the data it really needs and skip over the remainder of the XML stream. With SAX, the parser passes all available information to the client application, which will then have to either use or discard the information.

Readers are based on .NET Framework streams and work in much the same way as a database cursor. Interestingly, the classes that implement this cursor-like parsing model also provide the substrate for the .NET Framework implementation of XMLDOM parser. Two abstract classes, XmlReader and XmlWriter, are at the very foundation of all the .NET Framework XML classes, including XMLDOM classes, ADO.NET-related classes, and configuration classes. So in the .NET Framework you have two possible approaches when it comes to processing XML data. You can either use classes built directly on XmlReader and XmlWriter, or classes that expose information through the well-known XMLDOM object model. A more general-purpose introduction to readers of documents in the .NET Framework is available in my August 2002 Cutting Edge column.

The XmlReader Class

An XML reader supplies a programming interface that callers use to connect to XML documents and pull out the data they need. If you look at readers more closely, you realize that under the hood they work just like applications that fetch data out of a database. The database server returns a reference to a cursor object, which contains all the query results and makes them available on demand. The clients of an XML reader receive a reference to an instance of the reader class, which abstracts the underlying data stream and renders it as an XML tree. Methods on the reader class allow you to scroll forward through the contents, moving from node to node rather than from byte to byte or from record to record.

When viewed from the perspective of readers, an XML document is not a tagged text file but a serialized collection of nodes. Such a cursor model is specific to the .NET Framework; you will not find a similar programming API available elsewhere.

There are several key differences between readers and XMLDOM parsers. XML readers are forward-only, they have no notion of the surrounding nodes (siblings, parent, ancestors, children), and they are limited to reading. In the .NET Framework, reading and writing XML documents are two completely separate functions that require different, unrelated classes: XmlReader and XmlWriter. To be able to edit the contents of an XML document, you either use the XMLDOM parser (built around the XmlDocument class) or you design a custom class that aggregates two distinct entities like the reader and the writer under a common logical roof. Let's start by analyzing the programming features of the reader classes.

XmlReader is an abstract class that you can use to build more refined functionality. User applications are usually based on one of three derived classes: XmlTextReader, XmlValidatingReader, or XmlNodeReader. All these classes share a common set of properties (see Figure 1) and methods (see Figure 2). Note that sometimes the value that the properties actually contain depends on the reader class you are using in your code. Hence, the description of each property provided in Figure 1 refers to its intended goal, even though this may not reflect the role of the property in a derived reader class. For example, CanResolveEntity returns True only for XmlValidatingReader; it is set to False for any other type of reader class. Likewise, the behavior and the return value of some of the methods listed in Figure 2 are influenced by the type of the node the reader is working on. For example, all methods that involve attributes are void if the node is not an element node.

Figure 2 Methods of XmlReader-derived Classes (Close Figure 2)

Method | Description
Close | Closes the reader and sets the internal state to Closed
GetAttribute | Gets the value of the specified attribute; an attribute can be accessed by index, local, or qualified name
IsStartElement | Indicates whether the current content node is a start tag
LookupNamespace | Returns the namespace URI to which the given prefix maps
MoveToAttribute | Moves the pointer to the specified attribute; an attribute can be accessed by index, local, or qualified name
MoveToContent | Moves the pointer ahead to the next content node or end of file; the method returns immediately if the current node is already a content node such as non-white space text, CDATA, Element, EndElement, EntityReference, or EndEntity
MoveToElement | Moves the pointer back to the element node that contains the current attribute node; relevant only when the current node is an attribute
MoveToFirstAttribute | Moves to the first attribute of the current Element node
MoveToNextAttribute | Moves to the next attribute of the current Element node
Read | Reads the next node and advances the pointer
ReadAttributeValue | Parses the attribute value into one or more Text or EntityReference nodes
ReadElementString | Reads and returns the text from a text-only element
ReadEndElement | Checks that the current content node is an end tag and advances the reader to the next node; throws an exception if the node is not an end tag
ReadInnerXml | Reads and returns all the content below the current node, including markup information
ReadOuterXml | Reads and returns all the content of the current node, including markup information
ReadStartElement | Checks that the current node is an element and advances the reader to the next node; throws an exception if the node is not a start tag
ReadString | Reads the contents of an element or text node as a string; the method concatenates all the text until the next markup; for attribute nodes, it is equivalent to reading the attribute value
ResolveEntity | Expands and resolves the current entity reference node
Skip | Skips the children of the current node
Figure 1 Properties of XmlReader-derived Classes (Close Figure 1)

Property | Description
AttributeCount | Gets the number of attributes on the current node
BaseURI | Gets the base URL of the current node
CanResolveEntity | Gets a value indicating whether the reader can resolve entities
Depth | Gets the depth of the current node in the XML document
EOF | Indicates whether the reader has reached the end of the stream
HasAttributes | Indicates whether the current node has any attributes
HasValue | Indicates whether the current node can have a value
IsDefault | Indicates whether the current node is an attribute that originated from the default value defined in the DTD or schema
IsEmptyElement | Indicates whether the current node is an empty element with no attributes or value
Item | Indexer property which returns the value of the specified attribute
LocalName | Gets the name of the current node with any prefix removed
Name | Gets the fully qualified name of the current node
NamespaceURI | Gets the namespace URI of the current node; relevant to Element and Attribute nodes only
NameTable | Gets the name table object associated with the reader
NodeType | Gets the type of the current node
Prefix | Gets the namespace prefix associated with the current node
QuoteChar | Gets the quotation mark character used to enclose the value of an attribute
ReadState | Gets the state of the reader from the ReadState enumeration
Value | Gets the text value of the current node
XmlLang | Gets the xml:lang scope within which the current node resides
XmlSpace | Gets the current xml:space scope from the XmlSpace enumeration (Default, None, or Preserve)

The XmlTextReader class is designed to provide fast access to streams of XML data in a forward-only, read-only manner. The reader verifies that the submitted XML is well formed and throws an exception if it is not. The reader also performs a quick check to ensure that the referenced Document Type Definition (DTD), if any, exists. There's no case in which the XmlTextReader class validate the document contents against a schema or a DTD. The XmlTextReader parser is ideal for the quick processing of well-formed XML data accessible through file names, URLs, or open streams. If you need data validation, then you should use the XmlValidatingReader class.

An instance of the XmlTextReader class can be created in a number of ways and from a variety of sources including disk files, URLs, streams, and text readers:

XmlTextReader reader = new XmlTextReader(file);
Note that all the public constructors available require you to indicate the source of the data, be it a stream, a file, or whatever. The default constructor of the XmlTextReader class is marked as protected and, as such, is not used directly. As with all .NET Framework reader classes, once the reader object is up and running, you have to use the Read method to access the data. Readers move from their initial state to the first element only using the Read method. To move from any node to the next, you can continue using Read as well as a number of other more specialized methods including Skip, MoveToContent, and ReadInnerXml. To process the entire contents of an XML source, you typically set up a loop controlled by the Read method's return value: False when data is left to read and True otherwise.

Figure 3 shows a simple function that outputs the node layout of a given XML document. The function opens the document and sets up a loop to navigate through all the contents. Each time the Read method is called, the reader's internal pointer is moved one node forward. Although you will most likely be using the Read method to process element nodes, consider that in general when you move from one node to the next you are not necessarily moving between nodes of the same type. For example, the method does not move through attribute nodes. The reader's MoveToContent method lets you skip all the heading nodes and position the pointer directly on the first content node. Thus, the method skips over nodes of type ProcessingInstruction, DocumentType, Comment, Whitespace, and SignificantWhitespace.

Figure 3 Outputting an XML Document Node Layout (Close Figure 3)
string GetXmlFileNodeLayout(string file)
{
    // Open the stream
    XmlTextReader reader = new XmlTextReader(file);

    // Loop through the nodes and accumulate text into a string
    StringWriter writer = new StringWriter();
    string tabPrefix = "";

    while (reader.Read())
    {
        // Write the start tag
        if (reader.NodeType == XmlNodeType.Element)
        {
            tabPrefix = new string('\t', reader.Depth);
            writer.WriteLine("{0}<{1}>", tabPrefix, reader.Name);
        }
        else
        {
            // Write the end tag
            if (reader.NodeType == XmlNodeType.EndElement)
            {
                tabPrefix = new string('\t', reader.Depth);
                writer.WriteLine("{0}</{1}>", tabPrefix, reader.Name);
            }
        }
    }

    // Write to the output window
    string buf = writer.ToString();
    writer.Close();

    // Close the stream
    reader.Close();

   return buf;
}

Each node is given a type picked up from the NodeType enumeration. In the code shown in Figure 3, only two types of nodes are relevant: Element and EndElement. The output of the code duplicates the structure of the original document but discards attributes and text, preserving only the node layout. Let's assume I use the following XML fragment:

<mags>
   <mag name="MSDN Magazine">
   MSDN Magazine
   </mag>
   <mag name="MSDN Voices">
   MSDN Voices
   </mag>
</mags>
The final output looks like the following:
<mags>
   <mag>
   </mag>
   <mag>
   </mag>
</mags>
The indentation of child nodes is obtained using the reader's Depth property, which returns an integer value denoting the level of nesting of the current node. All the text is accumulated in a StringWriter object—an extremely handy stream-based wrapper for the StringBuilder class.

As I mentioned earlier, attribute nodes are not automatically visited by a reader that moves forward with the Read method. To visit the set of attributes of the current element node you must use a similar loop, but one that is controlled by the MoveToNextAttribute method. The following code accesses all the attributes of the current node (the one you selected with Read) and concatenates their names and values into a comma-separated string:

if (reader.HasAttributes)
while(reader.MoveToNextAttribute())
   buf += reader.Name + "=\"" + reader.Value + "\",";
reader.MoveToElement();

Once you're finished with node attributes, consider calling MoveToElement on the reader object. MoveToElement will "move" the internal pointer back to the element node that contains the attributes. To be precise, the method does not really "move" the pointer because the pointer never moved away from that element node during the attribute's navigation. The MoveToElement method simply refreshes some internal members making them expose values from the element node rather than the last attribute that was read. For example, the Name property returns the name of the last attribute that was read before you called MoveToElement and the name of the parent node read afterward. If you have no more work to do on the node and want to proceed to the next element once you're finished with the attributes, you don't really need to call MoveToElement.

Parsing the Contents of Attributes

In most cases, the content of an attribute is a simple string of text. However, it does not mean that string is the actual type of an attribute value. Sometimes the attribute value consists of the string representation of a more specific type, such as a Date or a Boolean, that you then convert into the native type using the methods of the static classes, XmlConvert or System.Convert. The two classes perform nearly identical tasks, but the XmlConvert class works according to the XML Schema Definition (XSD) data type specification and ignores the current locale.

Suppose that you have an XML fragment like the following:

<person birthday="2-8-2001" />
Let's also assume that, according to the current locale, the birthday attribute is February 8, 2001. If you convert the string into a specific .NET Framework type (the DateTime type) using the System.Convert class, everything will work as expected and the string will be transformed into the expected date object. By contrast, if you convert the string using XmlConvert, you'll get a parse error because the XmlConvert class does not recognize a correct date in the string. The reason is that in XML a date must have the YYYY-MM-DD format to be understood. The XmlConvert class works as a translator between CLR types and XSD types. When the conversion takes place, the result is locale independent.

In some situations, the value of the attribute comprises plain text along with entities. Of all the reader classes, only XmlValidatingReader is actually capable of resolving entities. The XmlTextReader class, although unable to resolve entity references, can separate text from entities when both are embedded in an attribute's value. For this to happen, you must parse the attribute's content using the ReadAttributeValue method instead of simply reading it through the Value property.

The ReadAttributeValue method parses the attribute value and isolates each constituent token, be it plain text or an entity. You call ReadAttributeValue repeatedly in a loop until the end of the attribute string value is reached. Since XmlTextReader does not resolve entities, there is not much you can do with the embedded entity other than writing your own resolver or maybe recognizing and skipping it. The following code snippet shows how you can call a custom resolver:

while(reader.ReadAttributeValue())
{
   if (reader.NodeType == XmlNodeType.EntityReference)
// Resolve the "reader.Name" reference and add
// the result to a buffer
buf += YourResolverCode(reader.Name);
   else
    // Just append the value to the buffer
buf += reader.Value;
}

The final value of a mixed content attribute is determined by accumulating the text in a global buffer. When the attribute value has been fully parsed, the ReadAttributeValue method returns False.

Manipulating XML Text

When working with XML markup text, some features can quickly become issues if not properly tackled. One such stumbling point is character conversion, which is sometimes necessary in order to transmit non-XML text in an XML stream of data. Not all the characters you may find available on a given platform are necessarily valid XML characters. Only the characters in the XML specification (www.w3.org/TR/2000/REC-xml-20001006.html) can be safely used for element and attribute names.

The XmlConvert class provides key functionality for tunneling non-XML names through XML between servers. When names contain invalid XML characters, the methods EncodeName and DecodeName can adjust them to fit into an XML name schema. Several applications, including SQL Server™ and Microsoft Office, allow and support Unicode characters in their documents. However, some of these characters are not valid as XML names. The typical circumstance, which demonstrates the importance of XmlConvert, occurs when you manipulate database column names containing blanks. Although SQL Server allows for a column name like Invoice Details, this would not be a valid name for an XML stream. The space must be replaced with its hexadecimal encoding, resulting in Invoice_0x0020_Details. The only valid form for hexadecimal sequences is _0xHHHH_, where HHHH stands for a four-digit hexadecimal value. Similar but different formats are left unaltered, though they can be considered logically equivalent. Here's how you can programmatically obtain that string:

XmlConvert.EncodeName("Invoice Details");

The reverse operation is accomplished by DecodeName. This method translates an XML name back to its original form by unescaping any escaped sequence. Notice that only fully escaped forms are detected. For example, only _0x0020_ is rendered as a blank, while _0x20_ would not be processed:

XmlConvert.DecodeName("Invoice_0x0020_Details");

In XML, whitespace found in the body of an XML document can either be significant or insignificant. Whitespace is said to be significant when it appears in the text of an element node or when it appears to be within the scope of a whitespace declaration:

<MyNode xml:space="preserve">
<!-- any space here must be preserved -->
•••
</MyNode>
In XML, the blanket term "whitespace" encompasses not just blanks (ASCII 0x20), but also carriage returns (ASCII 0x0D), line feeds (ASCII 0x0A), and tabs (ASCII 0x09).

The XmlTextReader class lets you control how whitespace is handled through the WhiteSpaceHandling property. This property accepts and returns a value taken from the WhiteSpaceHandling enumeration, which lists three feasible options. The default option is All and means that both significant and insignificant spaces are returned as distinct nodes—SignificantWhitespace and Whitespace, respectively. The option None indicates that no whitespace at all will be returned as a node. Finally, the option "Significant" discards all insignificant whitespace and only returns nodes of the type SignificantWhitespace. Note that the WhiteSpaceHandling property is one of the few reader properties that can be changed at any time and takes effect on the very next Read operation. The other "sensitive" properties are Normalization and XmlResolver.

Strings and Fragments

Programmers who cut their teeth on MSXML will certainly notice a key difference between the COM library and the .NET Framework XML API. The .NET Framework classes don't provide a native way to parse XML data stored in a string. Unlike the MSXML parser object, the XmlTextReader class doesn't provide any kind of loadXML method to build a reader from a well-formed string. This lack of a loadXML-style method is moot. There's no need for one because you can get the same functionality using a particular text reader—the StringReader class.

One of the XmlTextReader constructors accepts a TextReader-derived object and instantiates the XML reader from the contents of the text reader. A text reader class is a stream which has been optimized for character input. The StringReader class inherits from TextReader and uses an in-memory string as the input stream. The following code snippet shows how to initialize an XML reader with a well-formed XML string:

string xmlText = "...";
StringReader strReader = new StringReader(xmlText);
XmlTextReader reader = new XmlTextReader(strReader);
Likewise, using the StringWriter class in lieu of a TextWriter class, you can create XML documents in memory strings.

A special type of XML string is the XML fragment. The fragment is made of XML text in which the root-level rules for well-formed XML documents are not applied. As a result, an XML fragment differs from an ordinary document because it may lack a single root node. For example, the following XML string is a valid XML fragment but not a valid XML document since XML documents must have a root node:

<firstname>Dino</firstname>
<lastname>Esposito</lastname>

The .NET Framework XML API allows programmers to associate XML fragments with a parser context made of information like the encoding character set, the DTD document, namespaces, language, and whitespace handling:

public XmlTextReader(
   string xmlFragment,
   XmlNodeType fragType,
   XmlParserContext context
);
The xmlFragment parameter contains the XML string to parse. The fragType argument represents the type of the fragment and is given by the type of the fragment's root node(s). Only element, attribute, and document nodes are permitted as the root of a fragment and the parser context is expressed by the XmlParserContext class.

Validating Readers

The XmlValidatingReader class is an implementation of the XmlReader class that provides support for several types of XML validation: DTD, XML-Data Reduced (XDR) schemas, and XSD. DTD and XSD are official recommendations issued by the W3C, whereas XDR is the Microsoft implementation of an early working draft of XML Schemas.

You can use the XmlValidatingReader class to validate entire XML documents as well as XML fragments. The XmlValidatingReader class works on top of an XML reader—typically an instance of the XmlTextReader class. The text reader is used to walk through the nodes of the document, whereas the validating reader is expected to validate each single piece of XML according to the requested validation type.

The XmlValidatingReader class implements only a very small subset of the functionalities that an XML reader must expose. The class always works on top of an existing XML reader and mirrors many methods and properties. The dependency of validating readers on an existing text reader is particularly evident if you look at the class constructors. An XML validating reader cannot be directly initialized from a file or a URL. The list of available constructors comprises the following overloads:

public XmlValidatingReader(XmlReader);
public XmlValidatingReader(Stream, XmlNodeType, XmlParserContext);
public XmlValidatingReader(string, XmlNodeType, XmlParserContext);
A validating reader can parse any XML fragments accessible through a string or an open stream, as well as any XML document for which a reader is provided.

The list of methods for which the class provides a significant implementation is very short. In addition to Read there are the Skip and the ReadTypedValue methods. The Skip method jumps over the children of the currently active node in the underlying reader. (It's worth noting that you cannot skip over badly formed XML text.) The Skip method also validates the skipped contents. The ReadTypedValue method returns the value of the underlying node as a CLR type. If the method can map an XSD type with a CLR type, it will return it as such. If a direct mapping isn't possible, the node value will be returned as a string.

The validating reader is just what its name suggests—a node-based reader that validates the structure of the current node against the current schema. The validation occurs incrementally; there is no method that returns a Boolean value to indicate whether the given document is valid. You move around the input document using the Read method as usual. Actually, you use the validating reader as you would any other XML-based .NET Framework reader. At each step, the structure of the currently visited node is validated against the specified schema and an exception is raised if an error is found. Figure 4 contains a console application that takes the name of a file on the command line and then outputs the results of the validation process.

Figure 4 Console App (Close Figure 4)
using System;
using System.Xml;
using System.Xml.Schema;

class MyXmlValidApp
{
    public MyXmlValidApp(String fileName)
    {
        try {
            Validate(fileName);
        }
        catch (Exception e) {
            Console.WriteLine("Error:\t{0}", e.Message);
            Console.WriteLine("Exception raised: {0}",
                e.GetType().ToString());
        }
    }

    private void Validate(String fileName)
    {
        XmlTextReader xtr = new XmlTextReader(fileName);
        XmlValidatingReader vreader = new XmlValidatingReader(xtr);
        vreader.ValidationType = ValidationType.Auto;
        vreader.ValidationEventHandler += new
            ValidationEventHandler(this.ValidationEventHandle);

        vreader.Read();
        vreader.MoveToContent();

        while (vreader.Read()) {}

        xtr.Close();
        vreader.Close();
    }

    public void ValidationEventHandle(Object sender,
        ValidationEventArgs args)
    {
        Console.Write("Validation error: " + args.Message + "\r\n");
    }

    public static void Main(String[] args)
    {
        MyXmlValidApp o = new MyXmlValidApp(args[0]);
        return;
    }
}

The ValidationType property sets the type of validation you want—DTD, XSD, XDR, or none. If no validation type is specified (by using the ValidationType.Auto option), the reader automatically applies the validation it determines to be most appropriate for the document. The caller application is notified of any error through a ValidationEventHandler event. If you don't specify a custom event handler, an XML exception is thrown against the application. Defining a ValidationEventHandler method is a way to catch any XML exceptions caused by inconsistencies in the source document. Note that the reader's mechanisms for checking whether a document is well formed and for checking schema compliance are distinct. If a validating reader happens to work on a badly formed XML document, no event is fired but an XmlException exception is raised.

The validation takes place as the user moves the pointer forward with the Read method. Once the node has been parsed and read, it gets passed to the internal validator object for further processing. The validator operates based on the node type and the type of validation that has been requested. It makes sure that the node has all the attributes and the children it is expected to contain.

The validator object internally invokes two flavors of objects: the DTD parser and the schema builder. The DTD parser processes the content of the current node and its subtree against the DTD. The schema builder builds up a schema object model (SOM) for the current node based on the XDR or XSD schema source code. The schema builder class is actually the base class for more specialized XDR and XSD schema builders. What matters, though, is that XDR and XSD schemas are treated in much the same way and with no difference in performance.

If a node has children, another temporary reader is used to collect information about the children of the node so that the schema information for the node can be fully investigated, as you can see in Figure 5.

Figure 5 Using A Temporary Reader on Child Nodes
Figure 5 Using A Temporary Reader on Child Nodes

Note that although the signature of one of the XmlValidatingReader constructors refers generically to an XmlReader class as the underlying reader, that reader can only be an instance of the XmlTextReader class or a class which derives from it. This means that you cannot use any class which happens to inherit from XmlReader (such as a custom XML reader). Internally, the XmlValidatingReader class assumes that the underlying reader is an XmlTextReader object and specifically casts the input reader to XmlTextReader. If you use XmlNodeReader or a custom reader class, you will not get any error at compile time but an exception will be thrown at run time.

Node Readers

XML reader classes provide a way to process the contents of documents in an incremental way, one node after the other. So far, I've assumed that the source document is a disk-based stream or a string. However, nothing really prevents me from using an XMLDOM object as the source document. In this case, though, I would need a special class with an ad hoc Read method. For this purpose, the .NET Framework provides the XmlNodeReader class.

Just as XmlTextReader visits all the nodes of a specified XML stream, the XmlNodeReader class visits all the nodes that form an XMLDOM subtree. The XMLDOM class (XmlDocument in the .NET Framework) supplies XPath-based methods such as SelectNodes and SelectSingleNode. The net effect of such methods is that the matching node sets are loaded into memory. If you need to process all the nodes in the subtree, a node reader is more efficient as it just processes one node after the next in an incremental cursor-like manner:

// xmldomNode is the XML DOM node
XmlNodeReader nodeReader = new XmlNodeReader(xmldomNode);
while (nodeReader.Read()) {
    // Do something here
}

Using the XmlNodeReader class along with the XML DOM classes is also beneficial when you're processing an XMLDOM tree full of custom data excerpted from a configuration file (for example, web.config).

The XmlTextWriter Class

Creating XML documents in a programmatic way has never been particularly difficult. For years, developers did it by concatenating a few strings in a buffer and flushing the buffer out to a file when complete. But creating XML documents this way is effective only as long as you can guarantee that subtle errors will never find their way into the code stream. The .NET Framework provides for a more productive and elegant approach to creating XML documents by using XML writers.

An XML writer class outputs XML data to streams or files in a forward-only manner. More importantly, an XML writer guarantees—by design—that all the XML data it produces conforms to the W3C XML 1.0 and Namespaces recommendations. You don't even have to care about a single angle bracket or worry about the last element node that you left open. XmlWriter is the base abstract class for all XML writers. The .NET Framework provides only one actual writer class—the XmlTextWriter class.

To see the difference between XML writers and old-style writers, let's consider the following code to persist an array of strings:

StringBuilder sb = new StringBuilder("");
sb.Append("<array>");
foreach(string s in theArray) {
   sb.Append("<element value=\"");
   sb.Append(s);
   sb.Append("\"/>");
}
sb.Append("</array>");
The code loops through the elements of the array, prepares markup text, and accumulates it in a string buffer. The code is responsible for ensuring that the output is well formed and takes care of indentation, new lines, and namespace support. As long as the document to be created is simple and somewhat structured, there's nothing particularly wrong with this approach. However, when you have to support processing instructions, namespaces, indentation, formatting, and entities, the complexity of the necessary code grows exponentially, as does the likelihood of introducing errors and bugs.

An XML writer features write methods for each possible XML node type and makes the creation of XML output more logical and less dependent on the minutia of the markup languages. Figure 6 shows how to serialize an array of strings using the services of the XmlTextWriter class. Although comparably compact, the code that makes use of an XML writer is far clearer and better structured.

Figure 6 Serializing a String Array (Close Figure 6)
void CreateXmlFileUsingWriters(String[] theArray, string filename)
{
    // Open the XML writer (default encoding charset)
    XmlTextWriter xmlw = new XmlTextWriter(filename, null);
    xmlw.Formatting = Formatting.Indented;

    xmlw.WriteStartDocument();
    xmlw.WriteStartElement("array");
    foreach(string s in theArray)
    {
        xmlw.WriteStartElement("element");
        xmlw.WriteAttributeString("value", s);
        xmlw.WriteEndElement();
    }
    xmlw.WriteEndDocument();

    // Close the writer
    xmlw.Close();
}

Unfortunately, the XML writer isn't magic—it can't fix input errors. The XML writer does not check for invalid characters in element and attribute names and does not guarantee that any Unicode characters that are used will fit into the current encoding schema. As mentioned earlier, to avoid incorrect output, non-XML characters must be properly escaped when round-tripped as XML. This service is not provided by the writer.

In addition, when creating an attribute node, the writer does not verify whether an attribute with the same name already exists for the element. Finally, the XmlWriter class is not a validating writer and does not guarantee the output to be valid against any schema or DTD. A validating writer class is not currently available in the .NET Framework. However, a homemade implementation is available with the companion code of my book Applied XML Programming for Microsoft .NET (Microsoft Press®, 2002). You can download the code from http://www.microsoft.com/MSPress/books/6235.asp.

Figure 7 summarizes the feasible states for an XML writer. All the values come from the WriteState enumerated type. When you create a writer, its state is set to Start, meaning that you are still configuring the object and the actual writing phase has not begun. The next state is Prolog, which is reached as soon as you call the method WriteStartDocument to begin working. After that, the state transition depends on the type of document you are writing and its contents. The state remains Prolog as long as you add non-element nodes, like comments, processing instructions, and document type. When the first element node—the document root node—is written, the state changes to Element. The state switches to Attribute when you call the WriteStartAttribute method, but not when you write attributes using the more direct WriteAttributeString method. In that case, the state remains set to Element. Writing a closing tag moves the state to Content. When you are finished, you call WriteEndDocument and the state returns to Start until you begin another document or close the writer.

Figure 7 States for XML Writer (Close Figure 7)

State | Description
Attribute | The writer enters this state when an attribute is being written
Closed | The Close method has been called and the writer is no longer available for writing operations
Content | The writer enters this state when the content of a node is being written
Element | The writer enters this state when an element start tag is being written
Prolog | The writer is writing the prolog of a well-formed XML 1.0 document
Start | The writer is in an initial state, awaiting for a write call to be issued

The writer stores the output text in an internal buffer. Normally, the buffer is flushed, and the XML text only written when the writer is closed. By calling the Flush method, you can then empty the buffer at any time and write the current contents down to the underlying stream (which is exposed through the BaseStream property). Some working memory is freed, the writer remains open, and the operation can continue. Note, though, that the partially written storage file cannot be accessed by other processes until the writer is closed.

Attribute nodes can be written in two ways. The first involves the use of the WriteStartAttribute method to create a new attribute node and update the state accordingly. The WriteString method is then used to set the value of the attribute. WriteEndElement closes the writing phase for the node. Alternatively, you can use the WriteAttributeString method, which works when the state is Element and creates the attribute in a single shot. Likewise, the WriteStartElement writes the opening tag of the node, leaving you free to set the node's attributes and text at will. Normally, the closing tag of element nodes takes the compact form "/>". Use the WriteFullEndElement method if you want a full closing tag.

All the text that is passed to writing methods is automatically escaped if it contains sensitive markup characters such as the less-than (<) symbol. The WriteRaw method, though, gives you a chance to enter unparsed data into the stream. If you look at these two lines of code, the first outputs &lt, whereas the second outputs the unparsed < character:

writer.WriteString("<");
writer.WriteRaw("<");

Reading and Writing Streams

Interestingly, the reader and the writer classes provide methods to read and write streams of data even if they're encoded as Base64 or BinHex. The methods WriteBase64 and WriteBinHex feature a signature that is slightly different from other writing methods. Because they are stream-based, the methods function like an array of bytes rather than a string. The following code first transforms a string into an array of bytes and then writes it as a Base64-encoded stream. The static GetBytes method on the Encoding class performs the translation:

writer.WriteBase64(
   Encoding.Unicode.GetBytes(buf),
   0, buf.Length*2);

The code in Figure 8 persists an array of strings to a Base64-encoded XML stream while Figure 9 illustrates the final result as it appears in Microsoft Internet Explorer.

Figure 8 Persisting a String Array as Base64 (Close Figure 8)
using System;
using System.Text;
using System.IO;
using System.Xml;

class MyBase64Array
{
    public static void Main(String[] args)
    {
        string outputFileName = "test64.xml";
        if (args.Length > 0)
            outputFileName = args[0];    // file name

        // This is the array to convert to XML
        String[] theArray = {"Rome", "New York", "Sydney", "Stockholm",
                             "Paris"};

        CreateOutput(theArray, outputFileName);
        return;
    }

    private static void CreateOutput(string[] theArray, string filename)
    {
        // Open the XML writer
        XmlTextWriter xmlw = new XmlTextWriter(filename, null);
        xmlw.Formatting = Formatting.Indented;

        xmlw.WriteStartDocument();
        xmlw.WriteComment("Array to Base64 XML");

        xmlw.WriteStartElement("array");
        xmlw.WriteAttributeString("xmlns", "x", null, "dinoe:msdn-mag");
            foreach(string s in theArray)
        {
            xmlw.WriteStartElement("x", "element", null);
            xmlw.WriteBase64(Encoding.Unicode.GetBytes(s), 0,
                             s.Length*2);    // Unicode
            xmlw.WriteEndElement();
        }
        xmlw.WriteEndDocument();

        // Close the writer
        xmlw.Close();

        // Read it back!
        XmlTextReader reader = new XmlTextReader(filename);
        while(reader.Read())
        {
            if (reader.LocalName == "element")
            {

                byte[] bytes = new byte[1000];
                           int n = reader.ReadBase64(bytes, 0, 1000);
                string buf = Encoding.Unicode.GetString(bytes);

                Console.WriteLine(buf.Substring(0,n));
            }
        }
        reader.Close();

    }
}
Figure 9 String Array in Internet Explorer
Figure 9 String Array in Internet Explorer

Reader classes feature corresponding methods to decode Base64 and BinHex streams. The following code snippet shows how to decode the previously created file using the ReadBase64 method of the XmlTextReader class:

XmlTextReader reader = new XmlTextReader(filename);
while(reader.Read()) {
  if (reader.LocalName == "element") {
    byte[] bytes = new byte[1000];
int n = reader.ReadBase64(bytes, 0, 1000);
string buf = Encoding.Unicode.GetString(bytes);
    Console.WriteLine(buf.Substring(0,n));
  }
}
reader.Close();

The bytes-to-string translation is performed by the GetString method of the Encoding class. Although I presented code for the Base64 encoding schema, using BinHex is as easy as replacing the method names. This technique can be successfully used with any sort of binary data that can be expressed with an array of bytes, especially images.

Devising an XmlReadWriter Class

As mentioned earlier, XML readers and writers work in separate compartments: readers just read and writers just write. Suppose your application manages lengthy XML documents that contain volatile data. Readers provide a great way to read that content. Writers, on the other hand, are a very useful tool for creating that document from scratch. But if you want to read and write the document at the same time, you must resort to a full-fledged XMLDOM. If the document is particularly large, you have a problem. What, for example, can you do to read and write an XML document without loading it entirely in memory? Let's review how to set up a mixed type of streaming parser that works as a lightweight XMLDOM parser.

As for a read-only operation, you would use a normal XML reader to visit the nodes in sequence. The difference is that while reading you would have the opportunity to change attribute values and node contents by using an XML writer on top of the reader. You use the reader to read each node in the source document and an underlying writer to create a hidden copy of it. In the copy, you can add some new nodes, ignore or edit some others, and edit attribute values. When you are finished, you just replace the old document with the new one.

An efficient way to (bulk) copy blocks of nodes from the read-only stream to the writing stream is to use two methods of the XmlTextWriter class: WriteAttributes and WriteNode. The WriteAttributes method reads all the attributes available on the node that is currently selected in the reader. Next, the method copies attributes as a single string to the current output stream. Likewise, the WriteNode method does the same for any other type of node, except attribute nodes. Figure 10 shows a piece of code that uses these methods to create a copy of the original XML file, modified to skip some nodes. The XML tree is visited in the usual node-first approach, but only every other node is written out. You can aggregate the reader and the writer in a single new class and build a brand new programming interface to allow for an easy read/write streaming access to attributes or nodes.

Figure 10 Using the WriteNode Method (Close Figure 10)
XmlTextReader reader = new XmlTextReader(inputFile);
XmlTextWriter writer = new XmlTextWriter(outputFile);

// Configure reader and writer
writer.Formatting = Formatting.Indented;
reader.MoveToContent();

// Write the root
writer.WriteStartElement(reader.LocalName);

// Read and output every other node
int i=0;
while(reader.Read())
{
    if (i % 2)
        writer.WriteNode(reader, false);
    i++;
}

// Close the root
writer.WriteEndElement();

// Close reader and writer
writer.Close();
reader.Close();

My XmlTextReadWriter class does not inherit from XmlReader or XmlWriter but, instead, coordinates the activity of running instances of both classes—one operates on a read-only stream while the other works on a write-only stream. The methods of the XmlTextReadWriter class read from the reader and write to the writer, applying any requested change in the middle. The internal reader and writer are exposed through read-only properties called Reader and Writer, respectively. Figure 11 lists the methods of the new class.

Figure 11 XmlTextReadWriter Class Methods (Close Figure 11)

Method | Description
AddAttributeChange | Caches all the information needed to perform a change on a node attribute. All the changes cached through this method are processed during a successive call to WriteAttributes.
Read | Simple wrapper around the internal reader's Read method.
WriteAttributes | Specialized version of the writer's WriteAttributes method, writes out all the attributes for the given node, taking into account all the changes cached through the AddAttributeChange method.
WriteEndDocument | Terminates the current document in the writer and closes both the reader and the writer.
WriteStartDocument | Prepares the internal writer to output the document and add a default comment text and the standard XML prolog.

The class has a Read method which is a simple wrapper for the reader's Read method. In addition, it features a pair of WriteStartDocument and WriteEndDocument methods that initialize and finalize the internal readers and writers and do all the required I/O operations. Changes that involve nodes are performed directly by the client during the reading loop. For performance reasons, changes that involve editing on the attributes must first be registered using the AddAttributeChange method. All the changes to a node's attributes are temporarily stored in an internal table and flushed when WriteAttributes is called.

The code in Figure 12 shows a client that takes advantage of the XmlTextReadWriter class to change attribute values while reading. The full source code of the XmlTextReadWriter class is available, both in C# and Visual Basic® .NET, with this month's code download (see the link at the top of this article).

Figure 12 Changing Attribute Values (Close Figure 12)
private void ApplyChanges(string nodeName, string attribName,
    string oldVal, string newVal)
{
    XmlTextReadWriter rw = new XmlTextReadWriter(InputFileName.Text,
                               OutputFileName.Text);
    rw.WriteStartDocument(true, CommentText.Text);

    // Modify the root tag manually
    rw.Writer.WriteStartElement(rw.Reader.LocalName);

    // Prepare attribute changes
    // (Can Modify more attributes for the node)
    rw.AddAttributeChange(nodeName, attribName, oldVal, newVal);

    // Loop through the document
    while(rw.Read())
    {
        switch(rw.NodeType)
        {
            case XmlNodeType.Element:
                rw.Writer.WriteStartElement(rw.Reader.LocalName);
                if (nodeName == rw.Reader.LocalName)
                    // apply attribute changes
                    rw.WriteAttributes(nodeName);
                else
                    // deep copy
                    rw.Writer.WriteAttributes(rw.Reader, false);

                if (rw.Reader.IsEmptyElement)
                    rw.Writer.WriteEndElement();
                break;
        }
    }

    // Close the root tag
    rw.Writer.WriteEndElement();

    // Close the document and any internal resources
    rw.WriteEndDocument();
}

The XmlTextReadWriter class is more of a reader than a writer. The idea is that you use it to read the contents of an XML document but, if needed, you can also perform some basic updates. By basic updates, I mean changing the value of one or more existing attributes or the contents of a node, or adding new attributes or nodes. For more complex operations, nothing can take the place of XMLDOM parsers.

Summing it Up

Readers and writers are the foundation of XML data support in the .NET Framework. They represent a primitive API for all XML data access features. Readers work as a new and innovative type of parser that falls somewhere between the true power of XMLDOM and the fast and simple approach carried by SAX. Writers are a parallel universe made of tools designed to simplify the creation of XML documents. Although commonly referred to as pieces of the .NET Framework, readers and writers are actually completely separate APIs. In this article, I discussed how to accomplish key tasks using readers and writers and introduced the architecture of validating parsers. Unifying readers and writers in a single, all-encompassing class is still possible and results in a lightweight, cursor-like XMLDOM model.

For related articles see:
XML in .NET: .NET Framework XML Classes and C# Offer Simple, Scalable Data Manipulation
Implementing XmlReader Classes for Non-XML Data Structures and Formats
Applied XML Programming for Microsoft .NET by Dino Esposito (Microsoft Press, 2002)

For background information see:
XML Data: Overview of XML

Dino Espositois an instructor and consultant based in Rome, Italy. He is the author of Building Web Solutions with ASP.NET and ADO.NET, both from Microsoft Press. This article adapts some of the concepts expanded on in his book Applied XML Programming for Microsoft .NET (Microsoft Press, 2002). Reach Dino at dinoe@wintellect.com.

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


Debugging Tool: Build a Logging and Event Viewing Library to Help Debug Your .NET Framework-based App

Debugging Tool

Build a Logging and Event Viewing Library to Help Debug Your .NET Framework-based App

Daryn Kiely
This article assumes you're familiar with C# and Win32
Level of Difficulty 1 2 3
Code download available at: LoggingTool.exe (131KB)
SUMMARY

Building a basic, reusable application framework can make development quicker and easier. This allows you to focus more on the problems at hand and less on the repetitive tasks involved in building any application. In this article, the author presents a framework that provides facilities to access the registry and an extensible framework for logging messages to a console window or the Event Viewer. This reusable framework can be included as a library in your projects, allowing you to display an enhanced, color-coded message log and dynamically change logging levels.

Contents
Framework Exception
The Registry Class
The Logging Class Hierarchy
LogBase and LogMessage
EventViewerLog
ConsoleLog
Logger
Conclusion

A
reusable application framework can take care of the mundane tasks associated with every project, regardless of the programming language you have chosen. This framework usually takes shape early on as a way to store and retrieve application configuration data and as a logging mechanism. How you can build such a framework will be the focus of this article.

In the first section I'll cover storing and retrieving configuration data. I don't know about you, but I've gotten used to storing configuration data in the system registry. The Microsoft® .NET Framework makes it easier by providing the RegistryKey class, which I will expand to provide more functionality and default behavior that is not included as part of the .NET Framework registry classes.

Following the discussion of the registry classes, I'll cover a logging mechanism. Yes, you simply use the existing Event Viewer logging mechanism provided by the .NET Framework, but this can be cumbersome if you are writing and debugging an application. I prefer to see my messages as they are output, without having to scroll through the event viewer. Trace listeners are another option but they don't provide some of the functionality my examples afford you, such as the ability to dynamically change logging levels and logging messages as different levels.

Framework Exception

When defining my application framework, one of the first things I did was define an exception class to deal with exceptions that any internal classes will throw when an error condition occurs. I could have simply used one of the existing exception classes, but by defining my own, you'll get more granular control over which exceptions you want to handle.

Defining a custom exception class is actually very simple. All you have to do is derive from ApplicationException and provide the appropriate constructors. You can add more information to the exception and do your own processing, but for my purposes this would have been overkill.

The Registry Class

The registry classes supplied by the .NET Framework generally do just about everything a developer needs—they provide facilities to read and write to the registry. One thing I wanted to do was wrap the classes to hide some of the implementation details that deal with the commonly accepted rules of registry access. In addition to providing a wrapper for the existing .NET Framework registry classes, I wanted to add some functionality such as monitoring a registry key for changes (discussed later in this article).

There are two recommended places for an application to store registry information: HKEY_LOCAL_MACHINE\Software and HKEY_CURRENT_USER\Software. For client applications, the registry settings should be put under HKEY_CURRENT_USER, and systems applications (such as services) should put their entries under HKEY_LOCAL_MACHINE. If a nontrusted application attempts to modify the registry under HKEY_LOCAL_MACHINE, it will fail because of the built-in security in the .NET Framework and Windows NT®.

Your keys should be put under a manufacturer-specific key in the Software subkey of the registry location you chose to use. The constructor for the registry class will determine these values and open the proper key. It does this through the System.Windows.Forms.Application class and its static members, CommonAppDataRegistry and UserAppDataRegistry (see Figure 1). These static members get the CompanyName, ProductName, and ProductVersion from the assembly information that is stored as part of the application in order to create the base key.

Figure 1 Registry Construction (Close Figure 1)
public enum BASE_KEY
{
    SYSTEM,
    USER
};
public DRegistry()
{
    GetBaseSubKey(BASE_KEY.SYSTEM);
}
public DRegistry(BASE_KEY bkey)
{
    GetBaseSubKey(bkey);
}
public DRegistry(BASE_KEY bkey, bool RawAccess)
{
    switch (bkey)
    {
        case BASE_KEY.SYSTEM :
            m_CurrentKey = Registry.LocalMachine;
            break;
        case BASE_KEY.USER :
            m_CurrentKey = Registry.CurrentUser;
            break;
    }
    if (!RawAccess)
    {
        GetBaseSubKey(bkey);
    }
    else
    {
        m_BaseKey = m_CurrentKey;
    }
}
private void GetBaseSubKey(BASE_KEY bkey)
{
    switch (bkey)
    {
        case BASE_KEY.SYSTEM :
            m_CurrentKey = Application.CommonAppDataRegistry;
            break;
        case BASE_KEY.USER :
            m_CurrentKey = Application.UserAppDataRegistry;
            break;
    }
    m_BaseKey = m_CurrentKey;
    if (null == m_CurrentKey)
    {
        throw (new FrameworkException("DRegistry: Failed getting base
               subkey"));
    }
}

You will also notice in Figure 1 that I have provided three constructors. The default constructor assumes that all of the data will be placed under the HKEY_LOCAL_MACHINE\Software key with the CompanyName\ProductName\Version subkeys. The second constructor allows the user to choose between HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER. This selection is performed by using an enum that is defined within the registry class. The third constructor extends the functionality of the second constructor to allow the user to specify whether raw registry access is required. If the user does not want to be bound to the defined subkey, they can specify true for the second argument of this constructor. That will open the base key rather than the subkey.

The GetValue and SetValue methods offer pretty much the same functionality as their RegistryKey counterparts, but with a couple of exceptions. I wanted to provide default values for a key if it didn't exist in the registry. The GetValue methods of the registry classes do provide a default value implementation, but unfortunately these never get written back to the registry. I designed my GetValue methods to write the key if there is no current value, or if the value is of the wrong type. The other main difference is that the RegistryKey classes provide one implementation that either takes an object or returns an object. Since I wanted to have more granular control in the GetValue methods, I provided two implementations: one to be used for strings and one to be used for Int32 values. To keep the implementation consistent, I did the same thing with the SetValue methods.

The registry class needs to traverse the registry in order to be useful. When the registry object is created, the key that gets opened is stored in the m_BaseKey member. All navigation through the registry will begin at this key. By calling the GotoKey method the user can easily get a specific subkey, which will be created if it doesn't exist. The GotoKey method is nothing more than a wrapper around the CreateSubKey method of the RegistryKey class.

Finally, I wanted to add the ability to monitor a key and its subkeys. In C++ it was just a matter of making a call to a Win32® API function and supplying an event. Unfortunately this same functionality never appeared as a part of the registry classes in the Microsoft .NET Framework. However, the .NET Framework provides a means to use an existing set of APIs or COM components through Interop. Using the DllImport attribute, it is easy to call Win32 API functions.

There are a couple of catches when working with Interop in this way. The first is that the types that are passed to many of the Win32 calls don't match the native common language runtime (CLR) types. The only thing you can do in this case is to find the type that most closely matches the type that the API call is expecting and pass that as an argument. If a more complex type such as a structure is expected, you can create a structure and use the StructLayout attribute to force it to have the same byte layout as it would in C++.

The next catch is when there are constant values defined in the header files. For the most part, this is more of an inconvenience than anything else. All you need to do is create a const with the same value as the define. The downside to doing this is that if the define ever changes in the external DLL, your code will break.

By using the DLLImport attribute, I exposed the Win32 API function for monitoring registry keys (see Figure 2). In order to make this work, I needed to change the HANDLEs and BOOLs to IntPtrs and ints, respectively.

Figure 2 Interop Services for Monitoring Keys (Close Figure 2)
[DllImport("advapi32.dll")]
static extern Int32 RegNotifyChangeKeyValue(
    IntPtr hKey,          // handle to key to watch
    int bWatchSubtree,    // subkey notification option
    Int32 dwNotifyFilter, // changes to be reported
    IntPtr hEvent,        // handle to event to be signaled
    int fAsynchronous     // asynchronous reporting option
    );

public bool MonitorKey(WaitHandle WaitEv)
{
    const int NotifyFilter = 0xF; // Notify on any change
    IntPtr hkey = GetKeyHandle();
    if (0 ==
      RegNotifyChangeKeyValue(hkey,1,NotifyFilter,WaitEv.Handle,1))
    {
        return true;
    }
    return false;
}

private IntPtr GetKeyHandle()
{
    FieldInfo fi;
    IntPtr hkey;

    Type regKeyType = typeof(RegistryKey);
    fi = regKeyType.GetField("hkey",
      BindingFlags.Instance|BindingFlags.NonPublic);
    hkey = (IntPtr) fi.GetValue(m_CurrentKey);
    string s = hkey.ToString();
    Int32 i32 = hkey.ToInt32();
    return (IntPtr) hkey;
}

The next issue is how to actually get handles for the event and registry key. As it turns out, getting the event handle is trivial; there is a property on a WaitHandle that will return the operating system kernel handle. Getting the registry key handle, however, was not so easy. In this case I needed to resort to reflection. Using ILDASM, I found that the handle was actually stored as a private member called hkey (see Figure 3). From there I could use Reflection APIs to query its value, as shown in Figure 2. The main drawback is that I am accessing a private member of a class, which breaks the rules of object-oriented programming. In this case, there was no alternative.

Figure 3 Registry Key Handle in ILDASM
Figure 3 Registry Key Handle in ILDASM

The Logging Class Hierarchy

In designing the logging classes, I wanted to create a framework that other developers could extend. This would allow someone to add a specific logging mechanism, such as sending output to a printer. In order to accomplish this, I would have to create a base class for the logging mechanisms (LogBase) in addition to the logging class (Logger). I would also have to define a class for each of the loggers I wanted to implement, derived from the base class. The inheritance layout is shown in Figure 4. As a foundation, I chose to implement console logging and Event Viewer logging.

Figure 4 My Class Structure
Figure 4 My Class Structure

The framework solution is broken into four distinct projects: the logging base class, the Event Viewer logger, the console logger, and the main logger manager. I broke the project into logical areas of functionality to make it easier to manage and implement.

LogBase and LogMessage

LogBase is a key component that will not only have to implement the details of a thread, but also construct messages and pass them to the specific logging mechanisms. Each of the logging types must derive from LogBase and will have to provide functionality that is unique to the specific logging mechanism in question.

It may seem like an interface would have been the way to go instead of a base class. I didn't go down this path, though, because an interface is by definition a pure abstract base class and I wanted to add some more functionality to it. I wanted each logging mechanism to run as its own thread. This will enable me to run each of the logging threads at a lower priority and lessen the impact of slow logging mechanisms on the application. This also means I will have to implement a buffering mechanism in order to make this effective.

The buffering mechanism is also implemented as part of LogBase. It is nothing more than a Queue, which is defined in the C# collection classes. Everything that is put into this Queue will need to contain two parts, a severity code (which I will explain later) and the message to log. This requires me to create a new class to be called LogMessage, which is nothing more than an object with the information required to log the message and get and set methods for this information (see Figure 5).

Figure 5 LogMessage (Close Figure 5)
public class LogMessage {
    private string m_Message;
    public string Message {
        get {
            return m_Message;
        }
        set {
            m_Message = value;
        }
    }
    private SEV m_Severity;
    public SEV Severity {
        get {
            return m_Severity;
        }
        set {
            m_Severity = value;
        }
    }
    public LogMessage() {
        m_Severity = SEV.FATAL;
        m_Message = "";
    }
}

The buffering mechanism will also require a way to signal the thread to protect the Queue from concurrent access. In Windows®-based programming with Visual C++®, this is typically done with one of the standard synchronization objects, such as events. In C# the concept is no different, and there are several synchronization classes offered as part of the System.Threading namespace.

Critical sections can be implemented through the Monitor class. The Monitor class is nice because it can synchronize access to a specific memory location. The monitor manages the details of what you are trying to access and when to allow it—all you have to do is pass it an object. If another thread already has access to the object, the first thread will block until it is released with an Exit call. You must ensure that you call Monitor.Exit for each call to Monitor.Enter in order to avoid deadlock.

In addition to a Monitor, I will also use three ManualResetEvents to signal to the thread that there is something to do. The first ManualResetEvent is used to tell the thread that a message is currently on the queue (m_QueueDataEvent). When a thread puts a message on the queue, it sets the event. When the queue length reaches 0, this event is reset. The second ManualResetEvent is used to tell the thread to stop (m_StopEvent), and the final ManualResetEvent is passed to the DRegistry.Monitor method to signal when a change to the registry has occurred.

The Monitor class will control access to both the message queue and the event that is used to signal the thread that there is something to read. By ensuring that all access to the queue and signaling event is encased within the Monitor class's enter and exit methods, I will ensure that all queue operations are protected from concurrent access. If the access is allowed too long on the object, performance will degrade. To avoid this, I do as little work as possible within the Enter/Exit block.

As mentioned earlier, each log will run in its own thread. The entry point for the logging thread is the Run method. This thread spends most of its life blocked, waiting for a stop event, a data ready event, or a registry change event to occur. If the stop event occurs, the logging thread will terminate. If the registry change event occurs, the settings will be refreshed from the registry. If the queue data event occurs, the logger will process a single message from the queue and go back to its wait. If there are multiple messages in the queue, the wait will still return that there are messages to be logged. By cycling around, it gives an opportunity for other threads to add messages to the queue instead of having to wait for all messages to be drained.

Of course, there is a huge flaw with only providing buffered output. If a program crashes while there are some messages still in the buffer, you may never know the real cause of the crash. For this reason, I am providing the facility to force messages of particular severities to be flushed. I'll provide a function that allows the WriteLog method to query the logging facility to see if the message should be flushed. If it should, the thread will directly call the OutputString method, bypassing the queue. There is one subtle flaw with this approach in that flushed messages may precede buffered messages that were sent earlier. For this reason, I must break my earlier rule about spending as little time as possible in the Enter/Exit block. When a message is to be flushed, I must write all the messages that are currently in the queue, followed by the flush message.

Because both the main thread and the logging thread can access the queue, there's a potential race condition. This condition occurs when the thread doing the logging flushes the queue after the logging thread receives a signal that there is a message waiting, but before it reads the queue. If I do not check for this in the logging thread, it is possible that it will attempt to read a message from the queue after it has already been emptied. An easy solution to this problem is to check the queue length before the read, but after the Monitor has been acquired.

When a message is being logged, it should be formatted by the ConstructMessage method of the LogBase class. The formatting precedes the message with the severity of the message and the date and time. The severity string is one of five hardcoded values that are part of the SEV enum that I have defined as part of the logging base classes. I have found that this is more than I ever need. The date and time is taken from the DateTime class, which just happens to have a static property (Now) that returns a DateTime object set to reflect the current date and time on the computer.

Now that the logging features themselves are complete, I need to add a facility to get and store the logging levels. The first thing I want is a facility for a new logger to provide default settings for each of the logging levels. There are a couple ways to accomplish this, such as providing a virtual member function in LogBase or specifying an attribute that can be specified as part of a logging mechanism to provide default settings. Although both methods are equally effective, using attributes makes more sense. An attribute allows you to specify the characteristics of an object. The default logging levels are just that, characteristics of the logging mechanism. Using attributes also makes the job of the person writing the logging mechanism easier.

To create an attribute, the first thing you must do is write a class that derives from the Attribute base class. Typically, an attribute uses the naming convention XxxAttribute. However, when using the attribute, you do not need to specify the Attribute suffix; you can call the attribute either Xxx or XxxAttribute. The only other thing you need to know about attributes has to do with parameters and how they work.

Attributes can have two types of parameters, positional and named. (The DLL name in the DllImport attribute is an example of a positional parameter.) The positional parameters are defined when the attribute class provides a constructor that takes parameters. Positional parameters must be specified first in the list of parameters for the attribute and must be completely satisfied for at least one publicly accessible constructor.

Named parameters are more useful for the logging class attributes. Named parameters do not need to be specified at all, so they are perfect for specifying optional parameters. If they are specified, they can appear in any order in the parameter list. To specify a named parameter, all you need to do is provide a publicly accessible property.

Properties are a wonderful way to make classes more user-friendly. In C++, I would have provided get and set methods for a private member variable, but with C# I can make the members properties and simplify their usage. By providing get and set accessors, the member variables are exposed as properties.

The advantage of using properties is that code can be added to the get and set accessors to do things like range checking and manipulation. This allows the consumer of the class to have a simplified view of the members, while allowing the class implementer to perform special operations. Additionally, the class implementer can control the accessibility of the properties by providing only a get or set accessor.

It is important to know that the constructor for an attribute is called before the named parameters are set, which allows named parameters to be given default values in the constructor. The attribute class will specify OFF as a default value for all the logging levels. By using the named parameters, you can simply override the logging levels that make sense for your usage. The complete DefaultSeveritiesAttribute class is shown in Figure 6.

Figure 6 DefaultSeveritiesAttribute Class (Close Figure 6)
[AttributeUsage(AttributeTargets.Class)]
public sealed class DefaultSeveritiesAttribute : Attribute
{
    public DefaultSeveritiesAttribute()
    {
        fatal = "OFF";
        fail = "OFF";
        debug = "OFF";
        info = "OFF";
        init = "OFF";
    }
    private bool IsValid(string str)
    {
        switch (str)
        {
            case "ON" :
            case "OFF" :
            case "FLUSH" :
                return true;
            default :
                return false;
        }
    }
    private string fatal;
    public string FATAL
    {
        get
        {
            return fatal;
        }
        set
        {
            if (IsValid(value))
            {
                fatal = value;
            }
        }
    }
    private string fail;
    public string FAIL
    {
        get
        {
            return fail;
        }
        set
        {
            if (IsValid(value))
            {
                fail = value;
            }
        }
    }
    private string init;
    public string INIT
    {
        get
        {
            return init;
        }
        set
        {
            if (IsValid(value))
            {
                init = value;
            }
        }
    }
    private string info;
    public string INFO
    {
        get
        {
            return info;
        }
        set
        {
            if (IsValid(value))
            {
                info = value;
            }
        }
    }
    private string debug;
    public string DEBUG
    {
        get
        {
            return debug;
        }
        set
        {
            if (IsValid(value))
            {
                debug = value;
            }
        }
    }
}

Once the default logging levels are set up through the use of the attribute, I'll need a method that can get and make use of the attributes. This will be done in the GetDefaultLevel method, as shown in Figure 7. The first thing you will notice in this method is a call to GetCustomAttribute, which will either return an instance of the attribute class or return null if the class does not have the attribute specified. If the attribute has not been specified, I set the default logging level to OFF, otherwise I simply get the level from the properties in the attribute class.

Figure 7 GetDefaultLevel (Close Figure 7)
protected string GetDefaultLevel(SEV severity)
{
    DefaultSeveritiesAttribute attr = (DefaultSeveritiesAttribute)
    Attribute.GetCustomAttribute(this.GetType(),
        typeof(DefaultSeveritiesAttribute));
    if (null == attr)
    {
        return "OFF";
    }
    switch (severity)
    {
        case SEV.DEBUG :
            return attr.DEBUG;
        case SEV.FAIL :
            return attr.FAIL;
        case SEV.FATAL :
            return attr.FATAL;
        case SEV.INFO :
            return attr.INFO;
        case SEV.INIT :
            return attr.INIT;
    }
    return "OFF";
}

The rest of the logging level setup is very simple. Persistence is achieved either by retrieving from or saving the levels to the registry using the registry class described at the beginning of this article. At run time, the levels are stored in a static member variable that is called m_levels.

In order to allow you to set the program name, I made it a property of both the Logger and LogBase classes. When it is set, the ProgramName property will update the program name within the logger by calling the UpdateProgramName virtual method. In the case of Event Viewer, it will specify an Event Viewer source and also specify the console title in the event of the console log.

The LogBase also provides a virtual method that is called whenever the logging level is changed. Whenever the registry entries that specify the logging levels changes, the LogLevelChanged method is called. This allows the logger to do special processing when the levels are changing. An example of this processing is shown in the console log.

Finally, the LogBase must provide a virtual function that will be called whenever the logging mechanism is being shut down. This virtual function will provide the means for each logger to perform any required cleanup of the resources it is using. I will use this virtual function later to free the console and close the event viewer.

EventViewerLog

The Event Viewer log was pretty trivial to implement. Everything logged as part of the Event Viewer log is put in the application log. Using the program name property allowed me to specify the source of the log shown in Figure 8.

Figure 8 A Log
Figure 8 A Log

EventLog is one of the nice built-in features within the .NET Framework. The EventViewerLog class only had to use the EventViewer class, which is found in the System.Diagnostics namespace.

Your application must be registered with the event log service as a valid source of events before you can use the event logging classes. This is accomplished through the static method call, CreateEventSource. Once your application is registered, you can set the Source property to specify the text that will appear in the source column of the event viewer.

When the event log is created and the source is set, writing an entry is simply a matter of calling the WriteEntry method. There are several overloaded WriteEntry methods, allowing you to specify which of the EventViewer entries you want to fill in. I chose to fill in the type and the string data. Notice I had to map my event severities to three values in the event viewer (Error, Warning, and Information). These are the only values that Event Viewer supports that make sense to the framework I am creating.

In the EventViewerLog I have added exception handling. All of the Event viewer methods can potentially throw Win32 exceptions and ignoring them could cause your application to crash. The exceptions I handled are specifically related to the Win32 error returns that can be expected from the Event Viewer classes.

The final thing to notice in the definition of the Event Viewer class is how I used the DefaultSeveritiesAttribute (see the link at the top of this article for the full source code). Instead of taking the defaults provided by the system, I change them to more reasonable values for the event viewer by specifying this attribute and overriding the FAIL and FATAL members.

ConsoleLog

Why would I want to define a console log mechanism when System.Console pretty much provides the same functionality? I like logging messages of differing severities in different colors, and consoles are not typically included in a Windows-based program. The desired output from the console log is shown in Figure 9.

Figure 9 Output
Figure 9 Output

I looked at two possibilities for providing console logging: opening a console window or opening a Windows Forms window. Although it seems like it would be fairly trivial to open a Windows Forms window, it requires overhead that is quite complex. I would have to use a modeless window, provide a message pump, and manage a list of messages that are in the display. Why would I want to recreate all this functionality when a console window already provides what I need?

The first problem I experienced when writing the console log was the lack of console support in the C# libraries. I am used to being able to call AllocConsole, FreeConsole, and all the rest of the related console APIs from within my program. Although I could have used InteropServices as I did in the registry class, I chose to use managed C++ instead. This eliminated having to redefine the incompatible types or recreate defines. After all, one of the benefits of the .NET Framework is the interoperability among languages. This interoperability allows me to create a C++ class (ConsoleLog), derived from a C# class (LogBase), that I can use from another C# class (Logger).

Writing a managed C++ class is not very different from writing a standard C++ class. The first thing to familiarize yourself with is the __gc keyword, which tells the compiler that this is a garbage-collected managed class. The benefit to the programmer is that the .NET Framework will manage the lifetime of that class.

C++ provides the keyword #using, which tells the location of the files that contain the metadata classes that you want to import into your program. To properly use #using, you must define where to find the object that is referenced through the #using directive. This is done through the development environment on the project properties page. Selecting General | Resolve #using References in the C/C++ menu item allows you to set a search path for objects that are required through #using.

The first problem I had in writing the console log class was due to some issues with interlanguage interoperability. The base logging class defines the program name as a String type which would be fine, except that to invoke SetConsoleTitle I need a wchar_t*, which is not compatible with the managed String type. Fortunately, there is a function called PtrToStringChars that will convert the managed String into a wide character pointer. There is one small caveat in using this function. In order to ensure that the pointer will not be corrupted during garbage collection, you must pin the pointer.

During garbage collection, managed objects can potentially be moved to compact memory. The impact of this move is that the unmanaged pointer is pointed to a memory location that will become invalid. The __pin keyword ensures that this move does not happen in the first place.

One major problem with this technique is the behavior of the console when a user attempts to close it which can result in your application shutting down. To get around this, I disabled the ability to close the console window. Although this may sound trivial, I needed to do some tricks to make it happen—finding the window and deleting the close menu option from its system menu.

One final feature that I added to the console log was to display the console window only when there was output for that window. This involved opening and closing the window depending on what the current logging levels were set to. LogBase provides the facility to monitor what the current levels are and when they change. When there are no active logs, the console is closed. Alternatively, if the console is closed and a logging level is turned on, the console is opened. Other than the Close button and the String issue, developing the ConsoleLog class was straightforward. I used the Win32 console APIs to do everything I needed.

Logger

One of the goals of this project was to provide an extensible framework for logging. In order to do so, there has to be a way to initialize the existing logging types and add user-defined log types to the logger. I accomplished this by providing a base logger class that anyone can inherit from and a method that allows an instance of this class to be added to the list of known loggers.

The main logger class is responsible for managing the lifetime of the different logging mechanisms and routing the messages to all the existing loggers. Managing the lifetime of the existing loggers requires some coding by the client programmer. I have created a shutdown method to allow the program to tell the logging mechanism when things should be gracefully closed.

In order to manage the logging mechanisms, the Logger class must contain a list of the existing loggers. The .NET Framework provides several collection classes in System.Collection, which make the implementation of this list trivial. For the list of loggers, I decided on an ArrayList because it fit my needs and can easily be made thread safe. This list must be thread safe because any thread within an application may decide to add a new logging mechanism. To make a thread-safe version of an ArrayList, all you need to do is call the static method ArrayList.Synchronized.

Initialization can happen in many places within a program. I can either use a basic constructor, an Initialize method, or a static constructor. An initialization method works well as long as you remember to call it. A constructor has the advantage of being called by the .NET Framework. The .NET Framework also includes the concept of a static constructor, which is guaranteed to execute only once in the lifetime of a program, before the class is ever instantiated, and before any static members are initialized but after any static initializers. This ensures that the constructor will always be called exactly once if the object is used. This is great for the logging assembly since it allows singleton-like behavior without having to create a singleton.

Destruction is another interesting topic in the .NET Framework because the .NET Framework does not have the deterministic destruction that C++ developers are accustomed to using. In most instances, this isn't a problem, but I am creating threads and using resources that I will want to clean up properly, so I need to have control over when that shutdown takes place. In the logging framework, I created a method called StopLogger, which I expect the user to call before the application terminates. StopLogger uses a foreach loop to iterate through the list of loggers and call each one's StopLogger methods.

Register logger adds the logger to the list of known loggers and has the side effect of starting a thread for each logging type. It registers the Run method as the entry point for the thread and kicks it off with a call to ThreadStart. Once the thread is running, its priority is adjusted to run below the normal thread priority. This will help ensure that the logging mechanism does not significantly affect the performance of the running application. The thread methods are all part of the System.Threading namespace.

Conclusion

The concepts used in most object-oriented languages are not particularly difficult, but the implementation details often are. In writing the logging framework, most of my stumbling blocks arose from not being able to find system services that I have grown accustomed to having at my disposal. I have only scratched the surface of what is provided as part of the .NET Framework, but in doing so I hope I have provided a useful framework for you to use in your own projects.

For related articles see:
Visual Studio .NET: Managed Extensions Bring .NET CLR Support to C++
House of COM: Migrating Native Code to the .NET CLR

For background information see:
GC Class
Static constructors
System.Reflection Namespace

Daryn Kielyis a Software Development Manager for International Game Technology in Las Vegas. Daryn is currently working on real-time data processing using C# and the .NET Framework. He can be contacted at kiely@lvcm.com.


Metadata: Create a Database Schema Repository with Meta Data Services in SQL Server 2000

Metadata

Create a Database Schema Repository with Meta Data Services in SQL Server 2000

Alok Mehta and Ricardo Rodriguez
This article assumes you're familiar with T-SQL, XML, and XSL
Level of Difficulty 1 2 3
Code download available at: MetaDataServices.exe (114KB)
SUMMARY

SQL Server 2000 Meta Data Services is a repository technology that stores and manages metadata for SQL Server. Instead of building database schemas over and over, Meta Data Services allows you to freeze an entire schema for use in other projects. You can also use these schemas for training, testing, or debugging. In this article, the authors will review the various components of Meta Data Services and show how it can be programmed using a Visual Basic client, XML, and XSLT. They will also show you how to manage and manipulate your metadata by generating a simple database schema using a SQL Server repository.

Contents
Getting Started
Creating Repository Databases
Exporting the Metadata
Transforming the XML Document
Creating a SQL Schema from SQL Script
AppContent Example
Exporting AppContent Meta Data
Generating XSLT to Create SQL Script
Execute SQL Script to Generate SQL Server Schema
Conclusion

M
etadata is everywhere. It's an integral part of database schemas, object models, project descriptions, make files, cross references, reusable components, software/hardware configurations, business rules, and knowledge bases. Although metadata is very common, the tools and methodologies used to manage it are not. In many cases, developers find themselves designing and implementing similar database schemas over and over again for various different projects.

Furthermore, when an application is moved into a production environment, developers often like to freeze the version of database schema without the data so they can use the frozen schema for other purposes. Developers can start a new project with either a full or partially frozen schema or they can use it for documentation and training. They also might want to freeze the schema for testing, debugging, and optimizing the application without the production data in it. There are several ways to achieve these tasks. Developers can write SQL statements to make a backup of the database, remove all data from it, and use stored procedures to get information on the schema by writing custom solutions.

In this article, we'll discuss one of the hidden gems of SQL Server™ 2000, which will allow you to create a repository of database schema with ease—namely, Meta Data Services. We'll demonstrate the power of SQL Server 2000 Meta Data Services using XML so you can freeze the database schema and use parts of frozen schemas for other projects. We'll discuss the constituents of Meta Data Services and how they can be programmed using a Visual Basic® client, XML, and XSLT. We'll also walk through a simple data model in order to show you how it all works.

Getting Started

SQL Server 2000 Meta Data Services is an object-oriented repository technology that stores and manages metadata for SQL Server and its components. Meta Data Services is designed not only to store metadata but also to integrate with other tools and applications. It provides a solution for storing and managing data warehousing definitions, online analytical processing (OLAP) definitions, design data used in development tools, and any other type of metadata used in a programming environment. Meta Data Services offers much more than we will have time to cover in this article. Here are some of the features and services you can explore on your own.

Wide support for open standards Meta Data Services supports a variety of standards, such as COM-based interfaces, XML encoding, and Open Information Model (OIM). OIM supports standard metadata. XML encoding supports import and exporting of OIM metadata. For more information on OIM in Meta Data Services and XML in Meta Data Services, see Ways to Use XML in Meta Data Services.

Various information models Meta Data Services supports standard and user-defined metadata through the OIM and other information models. Model designers and programmers can use the SDK to create metadata-based applications and to build or extend information models that are the basis of shared metadata. For more information, see the Meta Data Services SDK.

A repository engine Meta Data Services provides a repository engine, which stores, consolidates, and retrieves metadata in repository databases. The repository engine is exposed as an object model that you can access using the Repository API.

A Repository API The Repository API that's included exposes repository engine functions and information model definitions through COM interfaces.

The Meta Data Browser This tool allows you to browse through registered databases. It can be used from within SQL Server 2000 or as a snap-in to Microsoft® Management Console (MMC). For more information, take a look at "Using Meta Data Browser".

As you can see, Meta Data Services provides a wide array of options that exceed the scope of this article. We'll focus on the power of the repository databases and their integration with XML within SQL Server 2000. We'll also show how you can create, browse, import, and export information within the repository databases using components developed by Microsoft. First, you'll need to install the Meta Data Services SDK version 3.0. Once you install the SDK, you will see several COM components installed in the directory, C:\Program Files\Common Files\Microsoft Shared\Repostry\. We will use some of the components in this directory.

Figure 1 Transforming a Schema
Figure 1 Transforming a Schema

While Meta Data Services in SQL Server 2000 can be used in several different ways, our approach uses its services as a transition step to assist you in creating transformed schema. For instance, when you start a new project, you may want a subset of the schema from an existing project rather than the entire schema. Figure 1 shows an overview of our approach. The next four sections describe each step in the process.

Creating Repository Databases

Sometimes developers are interested in freezing the schema of a specific version of a database. As you can see in Figure 1, Application Data Model 1 has many versions, but we want to freeze version 3. Our approach suggests creating the repository of database version 3 in this step. The repository engine is a service that provides basic functionality for storing and retrieving objects and maintaining the relationships among them. In this example, the database schema is the object used by the repository engine. Repository databases can be created in the following ways: using the COM component REPODBC.DLL in a Visual Basic client, or using the Enterprise Manager within SQL Server 2000. In this article, we will be using Enterprise Manager.

Exporting the Metadata

Once the repository is created, you can access it programmatically using the COM interfaces provided by the Meta Data Services SDK to export the schema as XML. SQL Server 2000 Meta Data Services supports XML by encoding repository databases for the purpose of importing, exporting, and publishing metadata in XML. You can also exchange metadata between two repository databases, between a repository database and an application, or between two applications that can interpret the same XML format. Exporting and importing data using XML is supported in the Meta Data Services using the COM interface. As mentioned earlier, we'll use the Meta Data Services facility within the Enterprise Manager to export the schema of the database. Although this schema can be useful for many purposes, our goal is to transform this XML into SQL script using XSLT.

Transforming the XML Document

Essentially, this next step automates the process of creating a database and the new schema. You can also use your custom DLL or third-party tools to transform the XML created in Step 2 of Figure 1 to create the target database. We use the power of XSLT to transform the schema for Application 2.

We've created a transformation into the exported XML based on the dbm namespace elements present. In this example, we have focused on creating a portable SQL script, which uses a set of generic language attributes.

We should mention that a metadata repository abstracts several information models for software design, and its creation is based on the OIM-supported models listed in Figure 2.

Figure 2 Information Models Used in Metadata (Close Figure 2)

OIM Groupings by Subject Areas | OIM Information Models | Namespace Identifier
Analysis and design model | Unified Modeling Language | uml
 | UML extensions | umx
 | Common data types | dtm
 | Generic elements | gen
Object and component description model | Component descriptions | cde
Database and data warehousing | Database schema elements | dbm
 | Data transformation elements | tfm
 | OLAP schema elements | olp
 | Record oriented legacy | rec
Knowledge management model | Semantic definition elements | sim

Since the goal here is to reproduce a database model based on its attributes, the logical choice to achieve this would be to work primarily on the elements related with the description of database schema elements. This means that we would be primarily processing elements from the dbm namespace. For this purpose, we have chosen two basic database elements that allow us to recreate a database up to a certain point: tables and indexes. We proceed to map them into the exported XML document from the elements dbm:DeployedTable and dbm:Index.

In order to separate the meta information and the information in the database itself, the metadata repository stores each definition separately. A relational association between the database structure and the data types it uses is made using IDs. For this reason, once we generate the first script from the exported XML, it does not contain any specific type information (such as nvarchar, integer, and so on), but simply the IDs related to its description. To generate this data, we have to process this XML and complete it dynamically. This means that once we've defined the basic layout of our script, we proceed to fill in the missing pieces inside the transformed page. In order to achieve this on-the-spot processing, the XSLT transformation creates a transformed document that presents the script in a user-friendly format, in this case HTML. Since the XPath expressions in an XSLT transformation are namespace aware, and in this case we need to match elements from other namespaces, the namespace needs to be declared within the XSLT file. The general flow is shown in Figure 3.

Figure 3 Transform Flow
Figure 3 Transform Flow

Once this document is generated, it is composed of an HTML tags frame. Inside it we will create the core document using JavaScript, which will create the final content of the document as well as retrieve the data types needed for the database script. This process is achieved using ADO from JavaScript through the creation of ActiveX® objects. Once we are able to retrieve these pieces, we'll dynamically write into the HTML document, as well as the other pieces of the page, in order to present the script. We will assume that the XML will get the reference to the stylesheet. This is done in the exported XML file header by adding the following at the beginning of the line:

<?xml-stylesheet type="text/xsl" href="http://C:\XML2Script.xslt"?>

Creating a SQL Schema from SQL Script

In this step we simply execute the SQL script to create the transformed schema in SQL Server 2000. This SQL script will automatically create the database needed for the new application (Application 2). Once executed, you are now ready to start the new project. Once the resulting document has been generated, it contains a UI where the script is shown inside an HTML tag. This could be used in several ways. Basically, the execution of the SQL script is left to the user's preference and can be performed either through a stored procedure, a query processor, or any other tool.

Before we begin describing the nitty gritty of our sample project, there's just one more thing we need to discuss. After starting with the new project, you may find yourself repeating the process. In other words, the data model of Application 2 may evolve into several versions and you may want to freeze one of its versions. This is shown in Step 5 of Figure 1.

AppContent Example

Our example starts with a simple data model (called AppContent) shown in Figure 4. This is a model of a typical Web site that has pages and content or menus on those pages. Each menu can have several menu items with their own properties. In this example, we will start a new project using the pages and contents tables shown in Figure 4. Figure 5 shows the Enterprise Manager with all the tables of AppContent.

Figure 4 Data Model
Figure 4 Data Model

The first step is to create the repository in SQL Server Enterprise Manager. On the local machine create the AppContent database with five tables—Pages, Contents, Images, Menu, and MenuItems. Configure the relationships among them, as shown in Figure 4. Navigate to Data Transformation Services | Meta Data within the Enterprise Manager. Right-click and select Import Metadata. Select the AppContent database within the local server, then click OK. You will see metadata from AppContent within the repository, as shown in Figure 6.

Figure 6 Metadata in the Repository (Close Figure 6)

Figure 5 Tables in Enterprise Manager
Figure 5 Tables in Enterprise Manager

Note that the repository offers several neat features such as tracking and merging versions of schemas and Data Transformation Services (DTS) packages. We simply use the repository as a transitional step in our approach. You can manage the schema within the repository, as well as doing many other tasks using COM interfaces provided by the Meta Data Services SDK 3.0. The following Visual Basic code programmatically creates the repository:

Public Sub CreateRepository()
    Dim oRepository As New RepositoryTypeLib.Repository
    oRepository.Create("SERVER= (local);DATABASE=AppContent","sa","")
    Set oRepository = Nothing
End Sub

RepositoryTypeLib.Repository is the COM interface available in the SDK. It offers many other methods and features. We cannot describe all of them here, so we encourage you explore them on your own. SQL Server creates several tables behind the scene (prefixed by "RTL") when the .create method is invoked.

Exporting AppContent Meta Data

The next step is to create an XML schema of the AppContent Meta Data. In the Enterprise Manager, navigate to Meta Data Services | Contents | Microsoft Data Warehousing Framework | OLE DB Database Schema | Name of your SQL Server Machine | AppContent. Then right-click and select Export to XML. You will be asked to name the exported file. Navigate to where you want to save that file and name it AppContent.XML. Its contents are shown in Figure 7. As mentioned before, this XML file contains a detailed description of the database, based on several OIM-supported information models (see Figure 2), including a description about the characteristics of each element selected to be exported as well as their subelements. Also note that you can export everything from the complete database down to any self-descriptive element.

Figure 7 Database Items (Close Figure 7)
<?xml version="1.0" encoding="windows-1252" ?>
    <oim:Transfer xmlns:oim="http://www.mdcinfo.com/oim/oim.dtd"
     xmlns:Umx="REPOS:Umx.dtd" xmlns:Tfm="REPOS:Tfm.dtd"
     xmlns:Gen="REPOS:Gen.dtd" xmlns:Uml="REPOS:Uml.dtd"
     xmlns:Dbm="REPOS:Dbm.dtd">
        <oim:TransferHeader Exporter="MSMDCXML" ExporterVersion="2.0"
          TransferDateTime="2002-09-04T12:18:54" />
        <Dbm:DeployedCatalog oim:id="_0" oim:objid="{{C376B717-BB5A-4504-
          83ED-256CCDB477FB},00000001}" RepositoryUpdatedBy="dbo"
          RepositoryUpdated="2002-08-10T18:51:02" URL="AppContent"
          name="AppContent">
          <Uml:NamespaceOwnedElement>
            <Dbm:Schema oim:id="_1" oim:objid="{{C376B717-BB5A-4504-83ED-
              256CCDB477FB},00000003}" oim:label="dbo" name="dbo">
       <Uml:ClassifierFeature />
           <Uml:NamespaceOwnedElement>
             <Dbm:DeployedTable oim:id="_2" oim:objid="{{C376B717-BB5A-
               4504-83ED-256CCDB477FB},00000041}" oim:label="Contents"
               name="Contents">
  •••
             <Dbm:Index oim:href="#_8" oim:objid="{{C376B717-BB5A-4504-
               83ED-256CCDB477FB},0000004D}"
               oim:label="Contents.PK_Contents" />
             <Dbm:DeployedTable oim:id="_10" oim:objid="{{C376B717-BB5A-
               4504-83ED-256CCDB477FB},00000053}" oim:label="Images"
               name="Images">
  •••
             <Dbm:Index oim:href="#_16" oim:objid="{{C376B717-BB5A-4504-
               83ED-256CCDB477FB},0000005F}" oim:label="Images.PK_Images"
               />
             <Dbm:DeployedTable oim:id="_18" oim:objid="{{C376B717-BB5A-
               4504-83ED-256CCDB477FB},00000065}" oim:label="MenuItems"
               name="MenuItems">
  •••
             <Dbm:Index oim:href="#_24" oim:objid="{{C376B717-BB5A-4504-
               83ED-256CCDB477FB},00000071}"
               oim:label="MenuItems.PK_MenuItems" />
             <Dbm:DeployedTable oim:id="_26" oim:objid="{{C376B717-BB5A-
               4504-83ED-256CCDB477FB},00000077}" oim:label="Menus"
               name="Menus">
  •••
             <Dbm:Index oim:href="#_31" oim:objid="{{C376B717-BB5A-4504-
               83ED-256CCDB477FB},00000081}" oim:label="Menus.PK_Menus" />
             <Dbm:DeployedTable oim:id="_33" oim:objid="{{C376B717-BB5A-
               4504-83ED-256CCDB477FB},00000087}" oim:label="Pages"
               name="Pages">
  •••
        <Dbm:Index oim:href="#_37" oim:objid="{{C376B717-BB5A-4504-83ED-
          256CCDB477FB},0000008F}" oim:label="Pages.PK_Pages" />
      </Uml:NamespaceOwnedElement>
      </Dbm:Schema>
    </Uml:NamespaceOwnedElement>
  </Dbm:DeployedCatalog>
</oim:Transfer>

Figure 8 shows the code needed to export the repository programmatically from a Visual Basic client into an XML file. (Just note that in a real scenario you should avoid the default "sa/no password" credentials.) The statement

Dim objMDExport As MSMDCXMLLib.Export
creates the export library from the MSMDC (Meta Data Coalition) XML component, which is also part of Meta Data Services. Likewise, the other objects—namely, RepositoryTypeLib.RepositoryObject, RepositoryTypeLib.Repository, and RepositoryTypeLib.ReposRoot—offer several methods to manipulate the repository. Finally, objMDExport.Export exports the metadata into an XML file. (For more information on the MDC, see OIM in Meta Data Services.)

Figure 8 Export from Visual Basic (Close Figure 8)
Public Sub ExportRepositoryData()
    Dim objMDExport As MSMDCXMLLib.Export
    Dim objMyObject As RepositoryTypeLib.RepositoryObject
    Dim objRepository As New RepositoryTypeLib.Repository()
    Dim objRoot As RepositoryTypeLib.RepositoryObject
    Dim objRepID As Integer

Call objRepository.Open("SERVER=(local);DATABASE=AppContent", "sa", "")
    objMyObject = objRepository.Object(objRepID)
    Call objMDExport.Add(objMyObject)
    Call objMDExport.Export("C:\XMLFile.xml")
    objMyObject = Nothing
    objRoot = Nothing
    objRepository = Nothing
    objMDExport = Nothing
End Sub

Generating XSLT to Create SQL Script

Once the exported XML is created, it should be associated with the transformation by including the following line at the beginning of the file:

<?xml-stylesheet type="text/xsl"
  href="http://C:MSDN\MetaData\XML2Script.xslt"?>
Then open it by using a browser. This way, the JavaScript can be executed in order to fill the missing parameters.

We then divide the script creation into two parts: XSLT processing and page generation with JavaScript. The XSLT process takes care of each element by applying the corresponding template to each element that we would like to make part of our schema—in this case, tables and indexes.

It is here that the JavaScript functions are created. Once executed, this script will return fully formed SQL syntax strings. These functions will form our final result: a SQL script capable of recreating the original database (or elements) exported from the metadata repository. This first part of the process will leave all type-specific information, which is unknown at this point, defined in terms of other functions that will retrieve it later during the second step of the process. Here's an example of the source data obtained from the XML exported file. Notice that there's no explicit data type information given:

<Dbm:DeployedTable oim:id="_2" oim:objid="{{91A6DEB7-0703-4DA4-980E-
  9317736AB009},00000091}" oim:label="Contents" name="Contents">
<Uml:ClassifierFeature>
<Dbm:DeployedColumn oim:id="_3" oim:objid="{{91A6DEB7-0703-
  4DA4-980E-9317736AB009},00000093}" oim:label="PageID"
  initialValue="0" Ordinal="1" name="PageID" Length="0"
  OctetLength="0" NumericPrecision="10" NumericScale="0"
  IsNullable="1">
<Uml:StructuralFeatureType/>
</Dbm:DeployedColumn>
And here's an example of the processing transformation:
<xsl:template match="Dbm:DeployedTable">
<!-- Checkup to remove previous existence of table objects, if
  so, then drop them -->
document.writeln('DROP TABLE [dbo].[<xsl:value-of
  select="@name"/>]');
document.writeln('GO\n');

For the second script creation part, the function called RetrieveDataTypeStr, created by the transformation, fetches the data type name from the database and from there it is included dynamically in the resulting HTML. The core part of the function is shown in Figure 9, but be sure to replace the server name, password, and user name to suit your configurations.

Figure 9 RetrieveDataTypeStr (Close Figure 9)
// Create conection using ADO
myConnection.Provider="SQLOLEDB.1";
myConnection.Open("Data Source=<xsl:value-of select="$DBServer"/>;User
  ID=<xsl:value-of select="$DBUsername"/>;Password=<xsl:value-of
  select="$DBPassword"/>;Initial Catalog=msdb");
// Querying the corresponding Object
sSQL = 'SELECT dbo.RTblNamedObj.[Name]' +
                    ' FROM dbo.RTblNamedObj' +
                    ' WHERE dbo.RTblNamedObj.IntID = ' +
                    ' (SELECT DstID' +
                    '  FROM dbo.RTblRelships' +
                    '  INNER JOIN dbo.RTblNamedObj' +
                    '  ON dbo.RTblRelships.OrgId = ' +
                    '  dbo.RTblNamedObj.IntID' +
                    '  WHERE SUBSTRING(ORGID, 2, 4) = " +
                    '  CONVERT(BINARY(8)," 0x' + sID + ')' +
                    '  AND RTblNamedObj.Name = \'' + sName+ '\')';
                myTable.Open(sSQL, myConnection, 2, 2);
                sValue = myTable.fields(0).value;

Executing the code produces the result shown in Figure 10. This is the SQL script that you can now copy and paste into the SQL Server 2000 Query Analyzer. The Submit Query and Download button on this page can automate the running of this script, which we won't cover here. Simply copying and pasting will do the trick.

Figure 10 Run the Function (Close Figure 10)

Execute SQL Script to Generate SQL Server Schema

The new schema is shown in Figure 11. You can start developing the new project now. Creating the database schema is very straightforward since execution scripts are natural to the database management. For this reason, we will not go into detail about how to create or execute SQL scripts, except to say that tools like Query Analyzer can ease the task of reproducing the database wherever you should need it.

Figure 11 Schema
Figure 11 Schema

Conclusion

Meta Data Services in SQL Server 2000 offers many more features than we have discussed here, such as DTS packages, metadata versioning capabilities, and the ability to perform configuration management. Besides Meta Data Services, other Microsoft technologies provide ways to create new application schemas from an existing database. For example, you can develop custom SQL scripts using ADO or ADO.NET or using the Schema Object Model (SOM), though most developers use SQL scripts and ADO to achieve this task. SOM, which is not yet widely used, is part of the MSXML 4.0 SDK. This SDK provides a navigable set of classes that directly reflect the W3C XML Schema definition language (XSD) specification. These classes enable you to walk through the elements of an XML Schema document and obtain information about the properties and declarations and about the relationships between them through either a Visual Basic or Visual C++® interface.

In this article, we have shown you a methodology to manage metadata using a SQL Server repository and its integration with XML. We then transformed the XML using XSLT into SQL scripts to generate the transformed database schema. We have purposely used a simple data model to illustrate our approach, but you can apply our approach to a large data model as well. The only thing that will change in using a different data model is the XSLT to transform the XML into SQL script.

For related articles see:
XML in Meta Data Services

For background information see:
http://www.w3.org/TR/xmlschema-0
Meta Data Services Architecture
Meta Data Services Software Development Kit

Alok Mehtais the CTO and Senior VP of AFS Technologies Inc. in Weston, MA where he is in charge of technology research and development. Alok has published several research papers on component-based software engineering and Web development.

Ricardo Rodriguez, currently Technology Director at SegurosdeVida.com, has a B.S. in Systems Engineering, and studies Electronics Engineering and Pure Mathematics. He is currently pursuing his MA in Information Technology at Harvard.


Visual Studio .NET: Building Windows Forms Controls and Components with Rich Design-Time Features, Part 2

Visual Studio .NET

Building Windows Forms Controls and Components with Rich Design-Time Features, Part 2

Michael Weinhardt and Chris Sells
This article assumes you're familiar with Visual Studio .NET and C#
Level of Difficulty 1 2 3
Code download available at: Design-TimeControls.exe (328KB)
SUMMARY

This is the second of two articles discussing the extremely rich design-time features of the .NET Framework. Part 1 discussed the basics, showing you where to start and how to extend your control implementation through attributes and interfaces, as well as their effects on the property browser, code serialization, and other controls. Part 2 continues the journey by concentrating on design-time functionality that you can implement beyond your components and controls, including TypeConverters, UITypeEditors, and Designers. It would be impossible to cover everything you can do in two short articles, which is a testament to just how all-encompassing and flexible the design-time capability of the .NET Framework is.

Contents
TypeConverters
Custom TypeConverters
Custom Type Code Serialization With TypeConverters
UITypeEditors
Designers
Design Time-only Properties
Conclusion

O
ur article in the April 2003 issue of MSDN® Magazine established some design-time fundamentals for components and controls, which we illustrated with a clock control sample. One of the major themes that we highlighted showed how design-time attributes and interfaces can be used to influence the relationship of a component or control instance to its design-time surroundings, especially with the property browser. When you select a control on a form, the property browser's entries are rendered from the design-time control instance. When you edit properties in the property browser, the control's design-time instance is updated with the new property values. This synchronicity isn't straightforward since the property browser only displays properties as text, even though the source properties could be of any type, from simple numbers and dates, to complex custom types. As values shuttle between the property browser and design-time instance, they must be converted from one type to another.

TypeConverters

Enter the type converter, whose main goal in life is to convert from one type to another and back again (for example, between types and strings). String-to-type conversion is exactly the right tool to support synchronization between the property browser and controls, and is achieved by using a type converter for each property displayed in the property browser (see Figure 1).

Figure 1 Property Browser/Design-time Conversion
Figure 1 Property Browser/Design-time Conversion

The Microsoft® .NET Framework offers System.ComponentModel.TypeConverter as the base implementation type converter, plus several further derivations of TypeConverter that support conversion to and from intrinsic .NET Framework types, including StringConverter, Int32Converter, and DateTimeConverter. These are alternatively called intrinsic type converters. If you know the type that needs conversion at compile time, you can then create an appropriate converter directly, as shown here:

// Type is known at compile time
TypeConverter converter =
    TypeDescriptor.GetConverter(typeof(int));
Or, if you don't know the type that needs conversion until run time, you can let System.ComponentModel.TypeDescriptor make the choice for you:
// Don't know the type before run time
object myData = 0;
TypeConverter converter =
    TypeDescriptor.GetConverter(myData.GetType());

TypeDescriptor provides information about a particular type or object, including methods, properties, events, and attributes. TypeDescriptor.GetConverter evaluates a type to determine a suitable TypeConverter. It does this by checking whether a type is adorned with an attribute that specifies a particular TypeConverter. It then compares the type against the set of intrinsic TypeConverter implementations. Finally, it returns TypeConverter if no other TypeConverters are found.

Because the property browser could display any controls or components with any type of property, it can't know those types in advance and consequently relies on TypeDescriptor.GetConverter to select the most appropriate TypeConverter for each property. Once a TypeConverter is chosen, the property browser and design-time instance can perform the required conversions using the same fundamental steps as those shown in Figure 2.

Figure 2 TypeConverter (Close Figure 2)
// Create the appropriate type converter
object myData = 0;
TypeConverter converter = TypeDescriptor.GetConverter(myData.GetType());

// Can converter convert int to string
if( converter.CanConvertTo(typeof(string)) ) {
  // Convert it
  object intToString = converter.ConvertTo(42, typeof(string));
}

// Can converter convert string to int
if( converter.CanConvertFrom(typeof(string)) ) {
  // Convert it
  object stringToInt = converter.ConvertFrom("42");
}

Some intrinsic TypeConverters can do more than just convert between simple types. To demonstrate, let's expose a Face property of type ClockFace, allowing developers to decide how the clock is displayed, including options for Analog, Digital, or Both:

public enum ClockFace {
  Analog = 0,
  Digital = 1,
  Both = 2
}

class ClockControl : Control {
  ClockFace _face = ClockFace.Both;
  public ClockFace Face {
    get { ... }
    set { ... }
  }
  •••
}
TypeDescriptor.GetConverter returns an EnumConverter, which examines the source enumeration and converts it to a dropdown list of descriptive string values (see Figure 3).

Figure 3 Enumeration Type
Figure 3 Enumeration Type

Custom TypeConverters

Intrinsic TypeConverters are useful, but they aren't enough if components or controls expose properties of custom types, like the clock control's HourHand, MinuteHand, and SecondHand properties (see Figure 4). The idea is to give developers the option to customize the clock's hands with color and width values. The unfortunate result of not having a custom type converter is shown in Figure 5.

Figure 4 Clock Hand Properties (Close Figure 4)
public class Hand {
  public Hand(Color color, int width) {
    _color = color;
    _width = width;
  }
  public Color Color {
    get { return _color; }
    set { _color = value; }
  }
  public int Width {
    get { return _width; }
    set { _width = value; }
  }
  private Color  _color = Color.Black;
  private int    _width = 1;
}

public class ClockControl : System.Windows.Forms.Control {
  public Hand HourHand { ... }
  public Hand MinuteHand { ... }
  public Hand SecondHand { ... }
  •••
}
Figure 5 Complex Properties
Figure 5 Complex Properties

Just as the property browser can't know what property types it will be displaying, the .NET Framework can't know what custom types you'll be developing. Consequently, there aren't any TypeConverters capable of handling them. You can, however, hook into the TypeConverter infrastructure to provide your own. To facilitate value synchronization, both property browser and design-time control instances need a type converter to translate to and from a Hand type, starting by deriving from TypeConverter:

public class HandConverter : TypeConverter { ... }
HandConverter must initially override CanConvertFrom, ConvertTo, and ConvertFrom to support the required conversion, like so:
public class HandConverter : TypeConverter {
  public override bool CanConvertFrom(ITypeDescriptorContext context,
                       Type sourceType) { ... }
  public override object ConvertFrom(ITypeDescriptorContext context,
                       CultureInfo info, object value) { ... }
  public override object ConvertTo(ITypeDescriptorContext context,
                       CultureInfo culture, object value,
                       Type destinationType) { ... }
}

CanConvertFrom lets clients know what it can convert from. In this case, HandConverter reports that it can convert from a string type to a Hand type, as shown here:

public class HandConverter : TypeConverter {
  public override bool CanConvertFrom(ITypeDescriptorContext context,
                       Type sourceType) {
    // We can convert from a string to a Hand type
    if( sourceType == typeof(string) ) { return true; }
    return base.CanConvertFrom(context, sourceType);
  }
  •••
}

Whether the string type is in the correct format is left up to ConvertFrom, which actually performs the conversions specified by CanConvertFrom. The clock control's string conversion expects a multi-valued string to be split into its atomic values, which are used to instantiate a Hand object (see Figure 6). Now, we need to provide the ability to convert from a Hand type back to a string, a task performed by ConvertTo (see Figure 7).

Figure 7 ConvertTo (Close Figure 7)
public class HandConverter : TypeConverter {
  public override object ConvertTo(ITypeDescriptorContext context,
      CultureInfo culture, object value, Type destinationType) {
    // If source value is a Hand type
    if( value is Hand ) {
      // Convert to string
      if( (destinationType == typeof(string)) ) {
        Hand hand = (Hand)value;
        string color = (hand.Color.IsNamedColor ?
                        hand.Color.Name :
                        hand.Color.R + ", " + hand.Color.G + ", " +
                        hand.Color.B);
        return string.Format("{0}; {1}", color, hand.Width.ToString());
      }
    }
    return base.ConvertTo(context, culture, value, destinationType);
  }
  •••
}
Figure 6 ConvertFrom (Close Figure 6)
public class HandConverter : TypeConverter {
  public override object ConvertFrom(ITypeDescriptorContext context,
                                     CultureInfo info, object value) {
    // If converting from a string
    if( value is string ) {
      // Build a Hand type
      try {
        // Get Hand properties
        string propertyList = (string)value;
        string[] properties = propertyList.Split(';');
        return new Hand(Color.FromName(properties[0].Trim()),
                        Convert.ToInt32(properties[1]));
      }
      catch {}
      throw new ArgumentException("The arguments were not valid.");
    }
    return base.ConvertFrom(context, info, value);
  }
  •••
}

You might have noticed that HandConverter doesn't implement a CanConvertTo override. The base implementation of TypeConverter.CanConvertTo returns a value of true when queried for its ability to convert to a string type. Since this is the correct behavior for the HandConverter, there's no need to override it. CanConvertFrom doesn't cope with strings, however, and must be overridden.

When TypeDescriptor.GetConverter checks for an attribute specifying which converter to use, it looks for TypeConverterAttribute, which is applied to a control in the following way:

public class ClockControl : Control {
  [ TypeConverter(typeof(HandConverter)) ]
  public Hand HourHand { ... }

  [ TypeConverter(typeof(HandConverter)) ]
  public Hand MinuteHand { ... }

  [ TypeConverter(typeof(HandConverter)) ]
  public Hand SecondHand { ... }
  •••
}
However, this is somewhat cumbersome. Luckily, moving the TypeConverterAttribute to the HandType class automatically ensures that TypeDescriptor.GetConverter returns HandConverter, resulting in much nicer code:
   [ TypeConverter(typeof(HandConverter)) ]
   public class Hand { ... }

   public class ClockControl : System.Windows.Forms.Control
   {
     •••
     public Hand HourHand { ... }
     public Hand MinuteHand { ... }
     public Hand SecondHand { ... }
   }
Figure 8 shows the effect of HandConverter.

Figure 8 HandConverter in Action
Figure 8 HandConverter in Action

While this is better than not being able to edit the property at all, there are still ways it could be improved. For instance, put yourself in a newbie's shoes. While it might be obvious to them what the first part of the property is, they might be a little disappointed by not being able to pick the color from one of those pretty dropdown color pickers. And what is the second part of the property meant to be? Length? Width? Degrees?

Figure 9 Subproperty Viewing
Figure 9 Subproperty Viewing

As an example of what we'd like to see, the Font type supports browsing and editing of its subproperties, as shown in Figure 9. This capability makes it a lot easier to understand what the property represents and what sort of values you need to provide. This feature is exposed by the .NET Framework for your design-time pleasure as an ExpandableObjectConverter from which you can derive to inherit property expansion mechanics. All that's needed in order to allow subproperty editing is to change the base type from TypeConverter to ExpandableObjectConverter:

class HandConverter : ExpandableObjectConverter { ... }
The results are shown in Figure 10.

Figure 10 Subproperty Editing
Figure 10 Subproperty Editing

Although we didn't have to write any code to make the Hand property expandable, we will have to write a little code to fix an irksome property update delay problem. In expanded mode, a change to the root property value is automatically reflected in the nested property value list. This occurs because the root property entry refers to the design-time property instance, while its nested property values are directly associated with the design-time instance's properties, illustrated in Figure 11.

Figure 11 Root and Nested Properties
Figure 11 Root and Nested Properties

When the root property is edited, the property browser calls HandConverter.ConvertFrom to convert the property browser's string entry to a new SecondHand instance, which results in a refresh. Yet changing the nested values only changes the current instance's property values, rather than creating a new instance, which doesn't result in an immediate refresh of the root property. TypeConverters offer a mechanism you can use to force new instance creation whenever instance property values change, which is achieved by overriding GetCreateInstanceSupported and CreateInstance. GetCreateInstanceSupported returns whether this support is available and, if so, calls CreateInstance to implement it, as the following code shows:

internal class HandConverter : ExpandableObjectConverter {
  public override bool GetCreateInstanceSupported
  (ITypeDescriptorContext context) {
    // Always force a new instance
    return true;
  }

  public override object CreateInstance
   (ITypeDescriptorContext context, IDictionary propertyValues) {
   // Use the dictionary to create a new instance
    return new Hand((Color)propertyValues["Color"],
        (int)propertyValues["Width"]);
  }
  •••
}

Custom Type Code Serialization With TypeConverters

While the Hand type now plays nicely with the property browser, it doesn't yet play nicely with code serialization. In fact, at this point it's not being serialized to InitializeComponent at all. To enable serialization of properties exposing complex types, you need to expose a public ShouldSerializePropertyName method that returns a Boolean:

   class ClockControl : Control {
     public Hand SecondHand { ... }
     private bool ShouldSerializeSecondHand() {
      // Only serialize non-default values
       return( (_secondHand.Color != Color.Red) ||
         (_secondHand.Width != 1) );
     }
     •••
   }

It doesn't matter whether your ShouldSerializePropertyName is public or private from the property browser's point of view, but choosing private removes it from client visibility. You can also implement property browser reset functionality programmatically, using ResetPropertyName method:

class ClockControl : Control {
  public Hand SecondHand { ... }
  private void ResetSecondHand() {
    SecondHand = new Hand(Color.Red, 1);
  }
  •••
}

Implementing ShouldSerialize lets the design-time environment know whether the property should be serialized, but we also need to write custom code to help assist in the generation of appropriate InitializeComponent code. Specifically, we need to return an InstanceDescriptor, which provides the information needed to create an instance of a particular type. The code serializer gets an InstanceDescriptor for a Hand by asking the Hand TypeConverter, as shown in Figure 12.

Figure 12 InstanceDescriptor (Close Figure 12)
internal class HandConverter : ExpandableObjectConverter {
  public override bool CanConvertTo(ITypeDescriptorContext context,
                                    Type destinationType) {
    // We can be converted to an InstanceDescriptor
    if( destinationType == typeof(InstanceDescriptor) ) return true;
    return base.CanConvertTo(context, destinationType);
  }

  public override object ConvertTo(ITypeDescriptorContext context,
      CultureInfo culture, object value, Type destinationType) {
    if( value is Hand ) {
      // Convert to InstanceDescriptor
      if( destinationType == typeof(InstanceDescriptor) ) {
        Hand     hand = (Hand)value;
        object[] properties = new object[2];
        Type[]   types = new Type[2];

        // Color
        types[0] = typeof(Color);
        properties[0] = hand.Color;

        // Width
        types[1] = typeof(int);
        properties[1] = hand.Width;

        // Build constructor
        ConstructorInfo ci = typeof(Hand).GetConstructor(types);
        return new InstanceDescriptor(ci, properties);
      }
      •••
    }
  }
  •••
}

InstanceDescriptors require two pieces of information in order to be useful. First, they need to know what the constructor looks like and second, what property values should be used if the object is instantiated. The former is described by the ConstructorInfo type, and the latter is simply an array of values which should be in constructor parameter order. After rebuilding the control, and if ShouldSerializePropertyName permits, all Hand type properties will be serialized using the information provided by the HandConverter-provided InstanceDescriptor:

class ClockControlHostForm : Form {
  •••
  private void InitializeComponent() {
    •••
    //
    // clockControl1
    //
    this.clockControl1.HourHand =
      new ClockControlLibrary.Hand
      (System.Drawing.Color.Black, 2);
    •••
  }
}

UITypeEditors

ExpandableObjectConverters help break down a complex multi-value property into nested lists of its atomic values. While this technique simplifies the editing of a complicated property, it may not be suitable for other complex properties, such as single-value properties that exhibit certain behavior. This might include properties that are hard to construct, interpret, or validate, such as regular expressions. The property may also be one of a list of values so large it would be difficult to remember all of them. It might even be a visual property, such as ForeColor, which is not easily represented as a string.

Actually, the ForeColor property satisfies all three points. First, it would be hard to find the color you wanted by typing comma-separated Int32s like "33, 86, 24" or guessing a named color, like "PapayaWhip." Second, there are a lot of colors to choose from, and colors are just plain visual.

In addition to in-place editing in the property browser, properties like ForeColor help the developer by providing an alternative UI-based property editing mechanism, accessed from a dropdown arrow in the property browser, as shown in Figure 13.

Figure 13 Color Properties
Figure 13 Color Properties

The result is a more attractive and intuitive way to select a property value. This style of visual editing is supported by a design-time feature known as a UITypeEditor, which you can leverage for a similar effect. There are two types of editor you can choose from: modal or dropdown. DropDown editors support single-click property selection from a dropdown UI attached to the property browser. This might be a nice way to enhance the clock control's Face property, allowing developers to visualize the clock face style as they make their selection (see Figure 14).

Figure 14 Custom Clock Face
Figure 14 Custom Clock Face

You begin implementing an editor by deriving a class from System.Drawing.Design.UITypeEditor:

class FaceEditor : UITypeEditor { ... }
The next step requires you to override UITypeEditor.GetEditStyle and UITypeEditor.EditValue, shown here:
class FaceEditor : UITypeEditor {
  public override UITypeEditorEditStyle
  GetEditStyle(ITypeDescriptorContext context) { ... }
  public override object EditValue(ITypeDescriptorContext context,
                                   IServiceProvider provider,
                                   object value) { ... }
}
The appropriate UITypeEditor, evaluated and returned by TypeDescriptor.GetEditor, is stored with each property, much like TypeConverters. In this case, when the property browser repaints itself to reflect a control selection in the designer, it queries GetEditStyle to determine whether it should show a dropdown button, an open dialog button, or nothing in the property value box when the property is selected. This behavior is determined by a value from the UITypeEditorEditStyle enumeration:
enum System.Drawing.Design.UITypeEditorEditStyle {
  DropDown, // Display dropdown UI
  Modal,    // Display modal dialog UI
  None      // Don't display a UI
}

Not overriding GetEditStyle is the same as returning UITypeEditorEditStyle.None, the default edit style. Neither option is useful if you have a pretty UI waiting in the wings. Luckily, the clock control does:

public class FaceEditor :
System.Drawing.Design.UITypeEditor {
    public override UITypeEditorEditStyle
GetEditStyle(ITypeDescriptorContext context) {
    if( context != null ) return
UITypeEditorEditStyle.DropDown;
    return base.GetEditStyle(context);
 }
  •••
}

ITypeDescriptorContext is passed into GetEditStyle to provide contextual information regarding the execution of this method. This includes:

  • The container and, subsequently, the designer and its components
  • The design-time instance represented by the property browser
  • A PropertyDescriptor type describing the property, including the TypeConverter and UITypeEditor assigned to the component
  • A PropertyDescriptorGridEntry type, which is a composite of the PropertyDescriptor and the property's associated grid entry in the property browser

Where GetEditStyle is used to initialize how the property browser behaves, EditValue actually implements the defined behavior. Whether the UI editor is dropdown or modal, you'll follow the same basic steps to edit the value. First, access the property browser's UI display service, IWindowsFormsEditorService, and create an instance of the UI editor, passing a reference to the editor service. Then pass it the current property value. Display the UI editor and then close it upon value selection. Finally, return the new property value from the editor. Figure 15 shows how the clock control implements these steps.

Figure 15 Editing the Value (Close Figure 15)
public class FaceEditor : UITypeEditor {
  •••
  public override object EditValue(ITypeDescriptorContext context,
      IServiceProvider provider, object value) {
    if( (context != null) && (provider != null) ) {
    // Access the property browser's UI display service,
    // IWindowsFormsEditorService
      IWindowsFormsEditorService editorService = (IWindowsFormsEditorService)
        provider.GetService(typeof(IWindowsFormsEditorService));

      if( editorService!= null ) {
        // Create an instance of the UI editor, passing a reference to
        // the editor service
        FaceEditorControl dropDownEditor = new FaceEditorControl(editorService);

        // Pass the UI editor the current property value
        dropDownEditor.Face = (ClockFace)value;

        // Display the UI editor
        dropDownEditor.EditorService = editorService;
        editorService.DropDownControl(dropDownEditor);

        // Return the new property value from the editor
        return dropDownEditor.Face;
      }
    }
    return base.EditValue(context, provider, value);
  }
}

When it comes to displaying the UI editor, we need to play nicely in the Visual Studio® .NET development environment, particularly regarding UI positioning in relation to the property browser. Specifically, dropdown UI editors must appear flush against the bottom of the property entry in the property browser and be sized to the width of the property entry. The property browser exposes a service, IWindowsFormsEditorService, to manage the loading and unloading of UI editors as well as their positioning inside the development environment. FaceEditor references it and calls IWindowsFormsEditorService.DropDownControl to display the FaceEditorControl, relative to the property entry. Once displayed, FaceEditorControl has the responsibility of capturing the user selection and returning control back to EditValue with the new value. This requires calling IWindowsFormsEditorService.CloseDropDown from FaceEditorControl, to which we pass the editorService reference, shown in Figure 16.

Figure 16 FaceEditorControl (Close Figure 16)
public class FaceEditorControl : UserControl {
  public FaceEditorControl(IWindowsFormsEditorService editorService) {
    •••
    _editorService = editorService;
  }

  private ClockFace                   _face = ClockFace.Both;
  private IWindowsFormsEditorService  _editorService = null;

  private void picBoth_Click(object sender, System.EventArgs e) {
    _face = ClockFace.Both;

    // Close the UI editor upon value selection
    _editorService.CloseDropDown();
  }
  private void picAnalog_Click(object sender, System.EventArgs e) { ... }
  private void picDigital_Click(object sender, System.EventArgs e) { ... }
  •••
}

The final step is to associate the FaceEditor with the Face property, accomplished by adorning the property with the EditorAttribute, as shown here:

[
  Category("Appearance"),
  Description("Determines which style of clock face to display"),
  DefaultValue(ClockFace.Both),
  Editor(typeof(FaceEditor),
    typeof(System.Drawing.Design.UITypeEditor))
]
public ClockFace Face { ... }
While dropdown editors are suitable for single-click selection, there are times when unrestricted editing is required. In such situations, you would use a modal UITypeEditor, implemented as a dialog box. While you follow the same logical steps as a dropdown editor, there are three minor implementation differences.
  • Returning UITypeEditorEditStyle.Modal from UITypeEditor.GetEditStyle
  • Calling editorService.ShowDialog(modalEditor) from EditValue to open the editor dialog
  • Not requiring an editor service reference to be passed to the dialog, since a Windows® Form can close itself
A modal Digital Time Format UITypeEditor, shown in Figure 17, is included in the code sample to demonstrate these differences (see the link at the top of this article).

Figure 17 Digital Time Format UITypeEditor
Figure 17 Digital Time Format UITypeEditor

Designers

So far, we have explored how properties are exposed to the developer at design time and discussed some of the key infrastructure provided by the .NET Framework to improve the experience, culminating in the UITypeEditor. While the focus has been on properties, they aren't the only aspect of a control that operates differently in design-time and run-time modes. In some situations, a control's UI might render differently between these modes. For example, the SplitterBar control displays a dashed border when its BorderStyle is set to BorderStyle.None, making it easier for developers to find on the form's design surface between two Panel controls (which also display a dashed border in the absence of visible one) (see Figure 18).

Figure 18 Border
Figure 18 Border

Because BorderStyle.None means "don't render a border at run time," the dashed border is only drawn at design time for the developer's benefit. Of course, if BorderStyle is set to BorderStyle.FixedSingle or BorderStyle.Fixed3D, the dashed border is not necessary.

What's interesting about both splitter and panel controls is that the dashed border is not actually rendered from either control implementation. Instead, this work is conducted on behalf of them by a Designer, another .NET Framework design-time feature that follows the tradition honored by TypeConverters and UITypeEditors of separating out design-time logic from the control.

Designers are not the same as designer hosts or the Windows Forms designer, although a strong relationship exists between designers and designer hosts. Every control or component you add to a form has a matching designer created for it and stored by the DesignerHost. This occurs at the same time components and controls are "sited." Like TypeConverters and UITypeEditors, TypeDescriptor does the work of creating a designer from TypeDescriptor.CreateDesigner. Adorning a type with the DesignerAttribute ties it to the specified designer. For components and controls that don't provide their own custom designers, the .NET Framework provides base designer implementations—ComponentDesigner and ControlDesigner, respectively, both of which implement IDesigner:

public interface IDesigner : IDisposable {
  public void DoDefaultAction();
  public void Initialize(IComponent component);
  public IComponent Component { get; }
  public DesignerVerbCollection Verbs { get; }
}

The technique used by panel and splitter controls could also be used by our clock control. The clock face is round at design time when the clock control is either Analog or Analog and Digital, making it difficult to determine where the edges and corners of the control are, particularly when positioning against other controls. A dashed border will help out in those design-time situations, as shown in Figure 19.

Figure 19 Clock Control
Figure 19 Clock Control

Because the clock is a control instead of just a component, we begin by deriving from the ControlDesigner base class since plain components would need a designer that is derived from ComponentDesigner, as shown here:

public class ClockControlDesigner : ControlDesigner { ... }
To paint the dashed border, ClockControlDesigner will need to override the Initialize and OnPaintAdornments methods:
public class ClockControlDesigner : ControlDesigner {
  •••
  public override void Initialize(IComponent component)
    { ... }
  protected override void OnPaintAdornments(PaintEventArgs e)
    { ... }
  •••
}
Initialize is overridden to deploy initialization logic that's executed as the control is being sited. It's also a good location to create a shortcut reference to the designed control:
public class ClockControlDesigner : ControlDesigner {
  private ClockControl   _clockControl = null;
  public override void Initialize(IComponent component) {
    base.Initialize(component);

    // Get clock control shortcut reference
    _clockControl = (ClockControl)component;
  }
  •••
}

While you could manually register with Control.OnPaint to add your design time UI, you'll find that overriding OnPaintAdornments is a better option because it is only called after the control's design-time/run-time UI is painted, letting you put the icing on the cake (see Figure 20). Simply adding DesignerAttribute to the ClockControl class completes the association:

[ Designer(typeof(ClockControlDesigner)) ]
class ClockControl : Control { ... }

Figure 20 Overriding OnPaintAdornments (Close Figure 20)
public class ClockControlDesigner : ControlDesigner {
  •••
  protected override void OnPaintAdornments(PaintEventArgs e) {
    base.OnPaintAdornments(e);

    // Don't show border if it does not have an Analog face
    if( _clockControl.Face == ClockFace.Digital ) return;

    // Draw border
    Graphics g = e.Graphics;
    using( Pen pen = new Pen(Color.Gray, 1) ) {
      pen.DashStyle = DashStyle.Dash;
      g.DrawRectangle(pen, 0, 0, _clockControl.Width - 1,
                      _clockControl.Height - 1);
    }
  }
  •••
}

Design Time-only Properties

Now the clock control is working the way we hoped in Figure 19. One way to improve on this is to optionally show the border. Adding a design time-only ShowBorder property will do the trick, since this is not a feature that should be accessible at run time. Implementing it on the control itself is not ideal because the control operates in both design-time and run-time modes. Designers are exactly the right location for design-time properties, especially because they offer the infrastructure that is needed to expose this property. You start off by adding the basic property implementation, just like you would on a standard control or component implementation (see Figure 21).

Figure 21 Border Property Implementation (Close Figure 21)
public class ClockControlDesigner : ControlDesigner {
  •••
  private bool   _showBorder = true;
  •••
  protected override void OnPaintAdornments(PaintEventArgs e) {
    •••
    // Don't show border if hidden or does not have an Analog face
    if( (!_showBorder) || (_clockControl.Face == ClockFace.Digital) )
        return;
    •••
  }

  // Provide implementation of ShowBorder to provide
  // storage for created ShowBorder property
  private bool ShowBorder {
    get { return _showBorder; }
    set {
      __showBorder = value;
      _clockControl.Refresh();
    }
  }
}

This isn't enough on its own, however, since the property browser won't examine a Designer for properties when the associated control is selected on the form. Somehow, we need to include ShowBorder in the list of properties returned to the property browser from TypeDescriptor.GetProperties. Fortunately, GetProperties uses the designer host's ITypeDescriptorFilterService that ultimately calls your designer's PreFilterProperties method, and it's this hook that you can leverage to dynamically add new properties before they are used, as shown in Figure 22. Now that we've added a design time-only property, it's shown in the property browser, but is unavailable at run time.

Figure 22 PreFilterProperties Method (Close Figure 22)
public class ClockControlDesigner : ControlDesigner {
  •••
  protected override void PreFilterProperties(IDictionary properties) {
    base.PreFilterProperties(properties);

    // Create design-time only property entry and add it to the
    // property browser's Design category
    properties["ShowBorder"] =
      TypeDescriptor.CreateProperty(typeof(ClockControlDesigner),
                                    "ShowBorder",
                                    typeof(bool),
                                    CategoryAttribute.Design,
                                    DesignOnlyAttribute.Yes);
  }
  •••
}
Figure 23 Overriding Properties
Figure 23 Overriding Properties

If you need to alter or remove existing properties, you can override PostFilterProperties. Pre/Post filter pairs can also be overridden for methods and events if you find it necessary. If you need to alter or remove existing properties, you can override PostFilterProperties (see Figure 23).

Conclusion

In these two articles we've shown how you can build and debug controls that target the .NET Framework design-time features, a must if you plan to integrate your components into the Visual Studio .NET development environment. In the April 2003 issue, we showed you how to use attributes and interfaces to enhance a control's relationship with the property browser. This article continued the discussion, introducing more advanced features like TypeConverters, UITypeEditors, and Designers. Taken together, you can create professional-level controls that will complement all of your .NET Framework-based projects.

For background information see:
Writing Custom Designers for .NET Components
Make Your Components Really RAD with Visual Studio .NET Property Browser
Creating Designable Components for Microsoft Visual Studio .NET Designers

Michael Weinhardtis a software engineer at SERF, a retirement fund company where he develops apps using the .NET Framework. He can be reached at mikedub@optusnet.com.au.

Chris Sellsis an independent consultant, speaker, and author specializing in distributed applications in the .NET Framework and COM. More information about Chris and his various projects is available at http://www.sellsbrothers.com.


Virus Hunting: Understand Common Virus Attacks Before They Strike to Better Protect Your Apps

Virus Hunting

Understand Common Virus Attacks Before They Strike to Better Protect Your Apps

Jason Fisher
This article assumes you're familiar with Visual Basic .NET
Level of Difficulty 1 2 3
Code download available at: VirusHunting.exe (115KB)
SUMMARY

Developer's machines can often be more vulnerable to viruses than the average corporate user because of their more frequent access to remote machines and shares, and the differing administrative privileges they maintain across mutiple machines. Reliance on antivirus software is fine as a first line of defense, but you need a basic arsenal of skills for securing the executables on your system and coping with viruses on your own. This article reviews proactive methods you can use to defend yourself against malicious executable code in resources, component libraries, scripts and macros, as well as how to avoid a handful of other potential vulnerabilities.

Contents
Preparing to do Battle
What Constitutes Executable Code?
A Wolf in Sheep's Clothing
Libraries Can Be Dangerous
Scripts and Macros—Increased Flexibility Brings Increased Risk
Virus Activation Methods
Registration Files
Path Vulnerabilities
On the Scrap Heap
The Best Offense is a Strong Defense
Conclusion

D
epending on how destructive their payload, computer viruses can result in a significant loss of data, time, and money. In the best scenario, you may just lose the time it takes to disinfect your computer. At worst, a mission-critical server may be reduced to little more than an expensive door-stop. Unfortunately, antivirus software isn't perfect, and there's always some lag between the emergence of a new electronic predator and the availability of virus definitions to protect against it. Moreover, many writers of today's more diverse and sophisticated computer viruses are taking the preemptive step of disabling or removing antivirus software as part of their activation.

I'm not suggesting that you shouldn't rely on your antivirus programs for day-to-day virus protection, especially since antivirus programs are very good at keeping you safe from known threats. However, if you've never had to fight a virus without your antivirus software, then it's only a matter of time before you will.

Because virus protection changes every day, I won't pretend to be exhaustive in my coverage. Rather, I'll attempt to arm you with information to help you start thinking like a virus writer.

Preparing to do Battle

In general, how do viruses work? Well, first, the author has to write the executable code required to carry out the virus's activation process. What does the author want the virus to accomplish? Should it reformat your hard drive? Delete JPG files? Mail copies of itself to your friends and coworkers? Making any of this happen requires executable code of some kind. Second, in order for this code to execute, the virus needs to be activated. The usual way a virus's executable code is run is the direct method: some unwary user receives an e-mail attachment called "Double-Click Here for Some Real Fun.exe" or something equally enticing. This runs the program and the virus is unleashed.

As easily avoided as this result seems to be—it still works far more often than it should—virus writers have discovered a number of other, less obvious techniques for getting a virus to take over your computer. Let's take a look at some of these techniques, beginning with the question of what constitutes executable code, then I'll move on to examine several sneaky activation methods. These activation methods are particularly important, as this is how you'll unhook viruses from your system in order to regain control of it following an infection.

What Constitutes Executable Code?

Of course, you know that .exe files are executable, as are other similar file types such as .cmd and .com. Because word processing documents can contain macro code to perform customized tasks, they too can run unsafe code. There are also many other file types that contain or launch executable code, and any executable code can be unsafe. In general, executable code falls into three broad categories: standalone programs, code included within resources or libraries, and script or macro code executed by an interpreter of some kind.

In a broad sense, a standalone program is pretty much any file type that relies on the operating system for it to execute. How do you know which these are? The answer lies in the Windows® registry. To battle against viruses on their own turf, you'll have to be very comfortable delving into the registry. So let's take a look at how executable programs are invoked.

Launch the Registry Editor, regedit.exe, and expand the HKEY_CLASSES_ROOT (HKCR) node, the operating system's repository for information on file associations and commands. Under HKCR, you'll see nodes representing all the file type extensions registered on your computer. Navigate down the tree until you locate the key named .exe. Select this node and observe that its default value (shown in the right-hand pane) is exefile. This is a pointer to another key under HKCR (the exefile key).

The exefile key contains a shell subkey. This is where a file type's available actions are defined. In OS terminology, these actions are known as verbs. For example, a Microsoft® Word document might have a print verb defined, which allows you to right-click the file in Windows and choose Print from the context menu. Expand the shell subkey for the exefile node to view the available verbs for EXE files. You'll probably see two or three different subkeys, depending on your system. The one to be concerned with is "open." Expand this node and select its command subkey. Each verb has its own subkey, and each of those keys in turn has its own command subkey. The default value in this subkey dictates exactly what happens when that verb is executed.

Double-clicking the file icon in Explorer has the same effect—it executes the default verb's command (open for EXE files). As you can see, for EXE files, the open command verb has a value of: %1 %*. If you remember MS-DOS® batch files, this probably looks somewhat familiar. The basic idea is that the path and file name of the EXE file you activated are substituted for the %1 parameter, while any switches or command-line parameters that go along with it are passed through the %* parameter.

So it would stand to reason that any other file types whose open verb evaluates to some flavor of %1 would tend to pose a risk. There are a number of these, and they're all potentially dangerous. You should consider that a virus writer knows that many people won't double-click a file with a .exe extension, and they probably wouldn't run a .bat file either, but what about running a .cmd file? What about .com, .pif, or .vbs? All of these file types have a default open verb of %1. A virus writer could simply change the .exe extension of his virus executable to .com, for example, and he's probably just increased the chances that the unsuspecting masses will run it. Particularly dangerous is the humble screen saver file type (.scr extension).

A Wolf in Sheep's Clothing

In your Registry Editor, compare the open verb's command default value for EXE and SCR files, respectively. As you can see, they're pretty much identical—"%1" %* for EXEs and "%1"/s for Screen Saver files. Screen savers are, as it turns out, standalone executables. The only difference between these two default verbs is the /S switch for the SCR file type. The intended purpose of the screen saver's "open" verb is to allow for testing a screen saver, and the screen saver executable interprets the /S switch accordingly. There's nothing to stop a virus writer from giving their application an .scr extension and then simply ignoring the /S switch passed to it when the user invokes the program.

Exploiting the popularity of screen savers is even easier because the caption of the screen saver's open verb is shown as "Test" in the context menu. A user thinks he's just testing a screen saver, but what he's actually doing is activating a virus. A particularly clever virus might even display an actual screen saver, preoccupying you while it destroys files on your hard drive in the background. This caption is stored in the default value for the open key itself. You might want to change this to "O&pen and Test", which is more accurate since in order to test a screen saver, the operating system has to "open" it. That way, users should realize that when they select that menu item, any executable code inside the screen saver will execute—a potentially unsafe condition in the event that code has been infected by a virus.

Libraries Can Be Dangerous

Executable code can also live inside resources or component libraries of many different varieties. These may not seem like obvious candidates for viruses, but they can certainly be exploited in that way. These file types include Dynamic Link Libraries (DLL), Control Panel Applets (CPL), various Type Libraries (TLB, OLB, and so on), ActiveX® Controls and COM Components (OCX, VBX, DLL, and others). This code isn't directly executable with a %1 command verb like EXE, CMD, COM, and others are, but this doesn't mean that the code can't be run.

Just about any function exported from a DLL can be invoked using a helper application called RUNDLL32.EXE. Consider the following example:

rundll32.exe shell32.dll,OpenAs_RunDLL c:\winnt\win.ini
The OpenAs_RunDLL function exported from SHELL32.DLL accepts one parameter, a file name. When invoked, it displays the Open With dialog box, as shown in Figure 1. You can select an application from the list, click OK, and the file name passed as a parameter is opened in the target application. There are a great many examples of this type of call in the system registry. You might want to take a look at some of them to get a better feel for how they work and what sort of system functions they provide. Just search the registry for RUNDLL32.

Figure 1 Exploiting the Helper App
Figure 1 Exploiting the Helper App

A virus could employ two possible attacks here. One would be to replace an existing DLL with a compromised version, in which a particular function is replaced by one of the same name but with altered functionality. Then, whenever the system invokes this function, instead of having the desired result, the virus is activated instead. The second approach is simply to write a DLL from scratch and invoke its functions using RUNDLL32.EXE when needed. This isn't quite as straightforward as invoking the code in an EXE file, but a DLL, OCX, TLB or other library file is more likely to be accepted by an unsuspecting user or to be overlooked by an antivirus program, so it may well be worth the greater effort on the virus author's part.

Scripts and Macros—Increased Flexibility Brings Increased Risk

Script code requires a script engine to interpret and run, but it can still be exploited. Scripts come in several flavors. Obviously, the Visual Basic for Applications (VBA) macros contained in Microsoft Office documents are some of the best known and most frequently exploited. Microsoft has done a lot to tighten the security of these macros, but it's still easy for a macro virus to do a lot of damage, and far too many users are careless with documents containing macros. Another increasingly popular script category is Windows Script Host (WSH) files. These files, usually with .wsf, .js, or .vbs extensions, carry a default file association which causes them to be executed, no questions asked, when users double-click them. As you might imagine, this can be disastrous. You may very well want to change the default action on your machine from "open" to "edit" to avoid any accidents. You can do this either in the registry or through the Folder Options dialog box shown in Figure 2.

Figure 2 Changing the Default Action
Figure 2 Changing the Default Action

Web applications may also carry dangerous scripts. As you probably know, client-side scripts and ActiveX controls are fairly limited in their access to the host system for security reasons, but there's a little-known file type, the HTML Application (with an HTA extension), which works like a client-side Web application without the same security restrictions. Its purpose is to allow developers to use their Web development skills to build rich applications using the Web browser metaphor. But again, the unsuspecting user can unleash all sorts of chaos by downloading and executing such a file without first examining its contents. All of these scripts and macros can be readily examined before they're executed, so you should pay particular attention to any you may come across on your system.

Virus Activation Methods

As I've mentioned already, the most common way for a virus to be activated is for a user to execute an e-mail attachment. Virus writers will do just about anything to make you open the attachment using this action, but most developers are savvy enough not to just run an unknown executable. Of course, just about all of us have done it at one time or another—particularly now that viruses can access address books and can assemble a credible-looking e-mail message, ostensibly from someone you know and trust. Be that as it may, as users have become more aware, virus creators have gotten more devious, and there is now a host of new methods for activating a virus on a computer that don't require any code to be explicitly executed. Understanding how this happens is important for untangling the mess left behind by infection.

Registration Files

Files with an REG extension, as you probably know, are system registration files that hold information to be integrated into the system registry. The problem with them is that they carry a default verb of "open" (with the caption "Mer&ge"). This means that if any registration file is double-clicked, it immediately dumps its contents directly into the system registry, without any confirmation required (depending on your operating system). Even without the possibility for abuse, this is an awful idea. Stop reading right now and change the default verb for REG files from "open" to "edit." As you saw in Figure 2, you can make this change using the Folder Options dialog box. You can also edit the default value for the shell subkey of the regfile key by right-clicking on the entry and choosing "Modify" (see Figure 3). Either approach will result in making edit the default action for this type of file.

Figure 3 Avoiding a PATH Vulnerability (Close Figure 3)

Why are registration files so dangerous? Consider this example. Last year, I had the opportunity to tangle with a virus on a friend's behalf. I noticed that every time I tried to launch any application, I got an error indicating that a particular executable (other than the one I'd asked for) couldn't be found. After some prodding, my friend confessed that in trying to "clean" his system himself, he'd come across this file and simply deleted it. But now he couldn't run anything at all. In theory, he was right to delete the virus executable, but clearly something was wrong with the registry settings for the EXE file type.

Of course, owing to his erasure of the virus program itself, he couldn't run any executable programs of his own, including the Registry Editor. The quick and dirty solution was to rename RegEdit.EXE to RegEdit.COM. I was counting on the fact that although the virus had obviously altered the registry settings for EXE files, it had probably left those for COM files alone. As luck would have it, this solution allowed me to run the Registry Editor. Just as expected, I saw that instead of "%1" %*, the open verb for EXE files had a command of "VirusExecutable.EXE %1". This allowed the virus program to run first any time the user attempted to execute any EXE program. The requested program was passed to the virus executable as a parameter, whereupon the virus could launch it, keeping the user largely in the dark about what was really going on. This is the danger of registration files: they can very easily affect changes of this sort. But if you understand the registry yourself, you can undo these modifications. A good option, but one beyond the scope of this article, is to consider establishing security control over certain key nodes in the registry. For more information on how to perform such tasks, go to A Crash Course: Editing the Windows NT Registry.

The EXE node is one of many locations in the system registry where you can find dangerous alterations. Two others are the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run and RunOnce keys. A REG file or a virus executable adds a value to either of these keys indicating the path and file name of any application or command the author wants to run on system startup. These items do not show up in the Startup group of the Start menu and are difficult to locate unless you're acquainted with this registry key. There are also Run and RunOnce keys in the HKEY_CURRENT_USER hive of the registry. Check both when you're wrestling with a virus.

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon is another vulnerable key. It contains several values which can be abused by viruses. The Shell key, for instance, determines the executable program used as the Windows shell, and it's usually set to "Explorer.exe", but it could be modified by a virus. The lesser known Userinit key, which ordinarily points to userinit.exe, can be modified to run any executable program on system startup quite beyond the user's level of awareness. A virus could point this value to its own executable, which could then launch userinit.exe normally, and the user would probably never notice. These are just a few of the more vulnerable keys to be aware of. If you're tangling with a virus on your own, some of these keys and values represent places to look for anything out of the ordinary. Of course, one person could never enumerate all keys that could possibly be exploited by a virus, but the point is to get familiar with the registry. Feel comfortable roaming through it and learn to establish some appropriate security settings for it.

Path Vulnerabilities

Another hazard is the PATH environment variable. As you know, you can collect all of your lengthiest and most frequently accessed system paths in a single PATH variable so that you don't have to type them out. If you put d:\Program Files\Microsoft Visual Studio\vb98\ into your PATH variable, for example, you can simply type "vb6" at a command prompt to launch Visual Basic® 6.0. There are numerous references to executable files in the system registry that also take advantage of this flexibility. The problem is that using this seemingly innocuous functionality is very lazy and fairly risky. Consider the following scenario.

Windows determines which executable application to use as the operating system shell from a value in the following registry location: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon. Within this key, the Shell value usually carries the value Explorer.exe. Makes sense, right? But what's missing is the path. Still, there's only one Explorer.exe—or should be—so why is it necessary to specify a path? Shouldn't Windows just be allowed to resolve the location through its ordinary path searching sequence (including the values in the PATH environment variable)? The answer is no! Variations of Nimda and CodeRed, among other culprits, have been known to infect and create new virus executables with the name Explorer.exe and to store them in common locations such as the root of the system partition. The virus can't overwrite your running instance of Explorer.exe, of course, because the OS has an exclusive lock on it, but the next time you reboot, the pathless setting in the Winlogon\Shell value will more than likely start up a bogus instance of Explorer.exe, loaded from the root of your system partition or another location that occurs earlier in your PATH variable than the system directory. If it's really nasty, now that an OS lock no longer protects %SystemRoot%\Explorer.exe, it may infect your original as well.

So, it's just laziness to leave the path off of this registry value. Again, stop reading right now and check your registry: if the value has no path, then change it immediately, as shown in Figure 3. Keep in mind that doing this may break some installer apps that don't use this properly. Also, start getting out of the habit of relying too heavily on the PATH environment variable. While you're at it, take a look at your PATH variable to make sure that a previous infection hasn't appended any other directories that have no business being there. This is a common exploit, but it's easy to avoid with a little care. You need to realize that another user on your network may have become infected (through any number of traditional methods, the most common is launching an infected attachment) and that the virus could copy an infected instance of Explorer.exe, or any other commonly used system file that suffers from a path vulnerability, to your computer via an open share. The next time you restart your computer, you're suddenly infected without ever having opened any attachments or running any questionable executable program yourself.

On the Scrap Heap

The last item I'll mention is the scrap object. There are actually two file types here (see Figure 4). Both are extremely dangerous because they can encapsulate executable code within a compound OLE document format. At your leisure, you should take a look at the registry settings that govern their open verbs to see how they work. Also, creating scrap executables isn't terribly difficult—see the related articles in the Summary box of this article.

Figure 4 Avoiding a PATH Vulnerability
Figure 4 Avoiding a PATH Vulnerability

There are two additional reasons these files are particularly risky, apart from the simple fact that they can hide executable code. First, they're often overlooked by antivirus software. Even if one of them is included in the list of executable application types, the other is often omitted. You should ensure that your antivirus program includes both file types.

The second reason is much more subtle. As it turns out, the SHS and SHB extensions are always hidden by Explorer, even if you've configured Windows to display file extensions. The reason is that the registry keys for these file types include an undocumented value, "NeverShowExt." If present, this value overrides global settings in Windows. For this reason, a virus writer can create a scrap object, give it an icon corresponding to an image, then rename it something like "Look at This Funny Picture.jpg." Its actual file name, of course, is "Look at This Funny Picture.jpg.shs," but to the unsuspecting user it looks exactly like any other image. By the time the realization dawns that the file wasn't an image at all, the damage is done. So, delete the "NeverShowExt" registry value from both keys now. Then you'll be better prepared to recognize these little scraps of danger.

The Best Offense is a Strong Defense

So what can you do about all of these nefarious little beasties? Clearly, total reliance on antivirus software is naïve, but at the same time, reacting to a virus after it's gotten a foot in the door is much more difficult. There should be a happy medium, a strategy by which you can avoid some infections before they occur, even though you don't know the exact nature of the virus trying to take over your system. To do that, you have to start thinking creatively.

Figure 5 illustrates one of many possible approaches to solving this problem. Here, in my Visual Basic .NET-based application, I've built a mechanism by which you may be able to prevent compromised programs from executing and thereby spreading the infection across an entire system or network. Before I explain what I've done, let me first say that this application is neither complete (you should add richer error-handling, for example, before deploying it to a production environment) nor infallible. Rather, it's a starting point, designed to get you thinking about proactive solutions to the virus problem.

Figure 5 Preventing Execution of Infected Files (Close Figure 5)
Imports System.Security.Cryptography
Imports Microsoft.Win32
Imports System.Text
Imports System.IO

Module VerifyExe

    Private Function ByteArrayToString(ByVal Data() As Byte) As String
        Dim Counter As Integer

        Dim SB As New StringBuilder(Data.Length)
        For Counter = 0 To Data.Length - 1
            SB.Append(Data(Counter))
        Next

        Return SB.ToString
    End Function

    Private Function CompareHashValues(ByVal FileName As String) As Boolean
        Const RegPath As String = "SOFTWARE\VerifyExe"

        Dim R As Registry
        Dim RegKey As RegistryKey = R.LocalMachine.OpenSubKey(RegPath,
            True)
        If RegKey Is Nothing Then
            'When this program runs for the very first time, this key
            'won't exist, so we'll have to create it.
            RegKey = R.LocalMachine.CreateSubKey(RegPath)
        End If

        'Attempt to read the MD5 hash from the registry for the specified
        'program.
        Dim ExeHashValue As Byte() = RegKey.GetValue(FileName)

        If ExeHashValue Is Nothing Then
            'The value didn't exist. This either means that it was removed
            'or that the specified program has never been run through this
            'utility. In either case, we need to create the value for next
            'time.
            RegKey.SetValue(FileName, HashData(FileName))
            'Now go ahead and run the program.
            Call Shell(Command(), AppWinStyle.NormalFocus)
        Else
            'The program has been run through our utility before. We need
            'to compare the hash value read from the registry with the ad
            'hoc hash value of the contents of the program as it exists
            'now.
            Dim AdHocHashValue As Byte() = HashData(FileName)
            If ByteArrayToString(ExeHashValue) = _
                ByteArrayToString(AdHocHashValue) Then
                'The two match, so run the program as expected.
                Shell(Command(), AppWinStyle.NormalFocus)
            Else
                MsgBox("This executable program appears to have been" _
                       & tampered with! " & _
                    vbCrLf & "Please reinstall the program from its" _
                       original source!", _
                    MsgBoxStyle.Critical Or MsgBoxStyle.OKOnly, _
                       "Potential Tampering Detected!")
            End If
        End If
    End Function

    Private Function HashData(ByVal FileName As String) As Byte()
        'Note that it's critical that the Shell\Open\Command registry key
        'for each file type must be configured to pass "%1" as the
        'argument; otherwise, paths and filenames containing spaces will
        'fail to parse correctly.
        Dim Fs As FileStream = New FileStream(FileName, FileMode.Open, _
            FileAccess.Read)
        Dim Result As Byte() = New _
            MD5CryptoServiceProvider().ComputeHash(Fs)
        Fs.Close()
        Return Result
    End Function

    Public Sub Main()
        If Command() <> "" Then
            Dim Fi As New FileInfo(Environment.GetCommandLineArgs(1))
            'If you wish, you can add additional executable extensions,
            'such as ".pif" or ".cmd". Bear in mind that the Shell function
            'only supports directly executable file types; if you replaced
            'the use of the Shell function with, for example, an API call,
            'you could actually apply this technique to any arbitrary file
            'type for which an association exists.
            Select Case Fi.Extension.ToLower
                Case ".exe", ".com", ".bat"
                    If Fi.Exists Then
                        Call CompareHashValues(Fi.FullName)
                    End If
            End Select
        Else
            'If no command line arguments are passed, error out. OR: you
            'could use this path to establish the correct file type
            'Shell\Open\Command registry associations!
            MsgBox("This application requires that the filename " & _
                and path to an vbCrLf & "executable program be passed " & _
                as a command-line argument", _
                MsgBoxStyle.Exclamation Or MsgBoxStyle.OKOnly, "Argument _
                Required")
        End If
    End Sub

End Module

I've coded the program to look for EXE, COM, and BAT extensions, but you can extend this to other file types if you like. My program looks in a particular registry location for a reference to the program. If such a value is found, it will contain an MD5 hash of the binary contents of the executable (see Figure 6). If no entry is found, it means that either the reference has been removed or the program hasn't been run on this system since the installation of the verification program. In either case, you have little choice but to go ahead and launch the program. First, however, you should perform an MD5 hash of the program's contents and store it in the correct registry location. Now, obviously, the program can't prevent a file that's already been infected (but hasn't been hashed yet) from executing. You could, however, add a confirmation dialog if you want, so that the user can make the final choice.

Figure 6 MD5 Hash Values (Close Figure 6)

If a hash entry does exist, then the requested executable program is rehashed on an ad hoc basis and the two values are compared. If the hash values match, then the program's contents haven't been altered since the first time it was run and hashed. Hashes that don't match suggest that the file has been altered. This, in turn, is a likely indication of virus activity since most viruses infect executable files by modifying their contents. The way they do this is beyond the scope of this article, but if you can determine when an executable program's contents have been altered, you can generally prevent it from spreading a virus across your system or from carrying out any further destruction. In this case, the utility does not allow the program to execute. Rather, it displays the warning indicating that potential tampering with an executable has been detected.

I haven't built an installation program for this utility, so before it will take effect, you have to hook it into your system. To do this, modify the Shell\Open\Command value in the registry for each file type you want to verify. For instance, to hook into the EXE file type, you'll change the default value of the HKCR\exefile\Shell\Open\Command registry key from "%1" %* to "path\VerifyExe.exe" "%1" %*.

Of course, you should substitute the actual path to the VerifyExe.exe. This will allow the utility program to run for all EXE files. It will then pass along the original requested EXE file and any command-line parameters or switches. Make the same registry modification for the COM and BAT extensions and for any other file types you want to verify. You might want to build these associations into the program itself or into an installation program.

Now, I know what you're thinking. What if a virus gets wise to this approach and decides to delete an application's registry value before infecting it? Then, the next time the user runs that application, it will be allowed to execute, thereby spreading the virus. Or what if a virus simply deletes VerifyExe.exe altogether? Both could happen, but the unfortunate truth is that this is just the way it is. No matter what you do, a virus may circumvent it. You can, of course, modify the source code I've provided to use a different location in the registry—an INI file, or any other storage mechanism you like. This will probably make it more difficult for viruses to avoid detection, however any solution that works will itself eventually be targeted by viruses. This is why, as I said earlier, many of today's nastier viruses are attempting to detect and incapacitate your antivirus software. Still, the solution offers fairly good preventive security, and it should get your creative juices flowing so you can devise your own methods as well.

Conclusion

Understanding how viruses take hold is the first step in knowing how and where to untangle them from your system once it has been compromised. As viruses become more sophisticated, you can expect them to become more aggressive toward your antivirus software. If it hasn't happened to you yet, you can be sure that sooner or later, you will have to battle a virus on your own. Remember, the best defense is a good offense. Here you've seen how to secure the contents of executable programs on your system, so you can be ready in the event of an attack. Along with that offense, up-to-date antivirus software, and an understanding of the relevant keys in the registry, you can go head-to-head with any invader that manages to sneak by you.

For related articles see:
Wrapping Malicious Code in Windows Shell Scrap Objects
Scrap Files Can Tear You Up
F-Secure Virus Descriptions
Introducing HTML Applications: DHTML Goes out of the Browser

For background information see:
http://www.microsoft.com/technet/security/
http://www.mcafee.com
http://www.wildlist.org
http://www.cert.org

Jason Fisheris a technical architect, freelance writer, and editor. By night, he's a crime-fighting caped crusader, battling viruses, DDoS attacks, and other intrusions with his own ruthless brand of computer justice! When not on the prowl, he can be reached at jasef@swbell.net.


Editor's Note: Get Ready For Microsoft Office 2003

Editor's Note
Get Ready For Microsoft Office 2003

Over the years, we've celebrated a lot of anniversaries in this column. Who can forget our "Ten Years of ENIAC!" Editor's Note back in June 1957? Or the "How Far We've Come: Five Years of the Altair 8800" issue back in January 1980? Or even the "Macintosh Interface: 15 Years Without an Update" Editor's Note page just four years ago?

Well, if you're expecting another one of those goofy anniversary columns, you can forget it right now. Instead, this Editor's Note marks another milestone: it's the first page written for the magazine using Microsoft Office Word 2003 (at least, that's its current codename). This is, at last count, the 11th word processor that's been used in the creation of an Editor's Note for MSDN Magazine and its predecessors. We've used 'em all—from Notepad to Word for MS-DOS to the original WinWord to today. There might even have been a WordStar or WordPerfect in there at one point, although our records don't go back that far.

You'll be seeing more about Microsoft Office 2003 in the months to come, here and elsewhere. There's a raft of new developer-centric stuff in there. You can order a prerelease copy today from http://www.microsoft.com/office/preview/orderbeta.asp.

Office 2003 still supports Visual Basic for Applications (VBA) projects unchanged, for the most part, from Office XP. However, you'll also be able to write Visual Basic .NET or C# code and run it from within Office 2003. This will actually take the form of a new technology codenamed "Visual Studio Tools for Office." Why is this a big deal? In addition to the existing VBA macro technology, you'll get the full benefit of the Visual Studio .NET environment: language improvements like structured exception handling, inheritance, and polymorphism; visual UI design; XCOPY deployment of Office code; and connection to the Internet and to data through integrated XML-based Web Services functionality.

Word 2003 and Excel 2003 will expand upon the XML support found in Word 2002 (which was limited to saving HTML format with XML data islands). In Word 2003, you'll be able to create and save actual XML documents in several formats, including documents that conform to XML schemas. In addition, you'll be able to apply XSL transforms to your XML data as you're saving it.

A third component of Office 2003 that will hold lots of interest for power users and developers will be Microsoft InfoPath, which will provide a brand new GUI-driven way of creating, filling out, and submitting XML-based forms. The user doesn't even have to know that they're using XML! Developers will be able to write business layer code to process the XML data that's been entered, design new forms, and even create templates. The package comes with a bunch of precreated business forms, like the old favorite Travel Itinerary, as shown here.

The final new feature of Office 2003 that we have the space to point out here is its built-in support for XML-based Web Services. You can already do this to some extent with the Office XP Web Services Toolkit 2.0, but this will be available as an Office 2003 add-on. Integrating an XML-based Web Service into your Office 2003 solution will be as easy as selecting the appropriate Web Service reference from a dialog, just as you can with Visual Studio .NET. Since advanced XML support will be baked into the product, you'll be able to take advantage of these Web Services within Office-based VBA solutions just as you make use of COM libraries today.

In upcoming months, we'll be discussing Office 2003 (or whatever its final name may be) here. To prepare for all this excitement, you should refamiliarize yourself with XML-based Web Services and how to use them in Visual Studio .NET. You can also check out the new Office 2003 center on MSDN Online. Happy testing!

—J.T.

Active Directory, ActiveX, DirectX, JScript, Microsoft, Microsoft Press, MSDN, MS-DOS, Visual Basic, Visual C++, Visual Studio, Windows, Windows NT, and Win32 are registered trademarks of Microsoft Corporation. Other trademarks or tradenames mentioned herein are the property of their respective owners.

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


New Stuff: Resources for Your Developer Toolbox

New Stuff
Resources for Your Developer Toolbox
Theresa W. Carey

Contents
Stitch ASP Apps to ASP.NET
Graphing with .NET
Detag HTML Pages
Cross-platform Data Exchange
Is that License Up To Date?
Programmer's Text Editor
Web Content Management
Managing Patches
Share Session Variables
Barcodes for the .NET Framework
Integrate with QuickBooks
Interactive HTML Help
The Bookshelf

Stitch ASP Apps to ASP.NET

Consonica Ltd. has announced StateStitch, which enables ASP.NET technology to be used with existing ASP applications. ASP.NET offers a more powerful feature set, but since ASP and ASP.NET applications cannot share session data, existing applications would otherwise need to be completely rewritten. StateStitch solves this incompatibility. ASP and ASP.NET applications run side by side and sessions are shared between the two environments. Developers can now add Microsoft® .NET Framework features while continuing to make use of their existing applications.

Graphing with .NET

Graphics Server Technologies recently announced that it is shipping Graphics Server .NET, a component that provides managed code support for the .NET Framework. Graphics Server .NET provides a collection of business and scientific graphing features, including a variety of graph and chart types, property pages to customize graphs, statistical overlays, and limit lines. Graphs can have multiple data sources in a graph or even in a series. The package includes a resource center with Visual Basic® and C# code examples and extensive documentation.

Detag HTML Pages

JafSoft Ltd. has released Detagger 2.0 for Windows, an HTML-to-text converter and markup remover. Detagger allows you to extract text from your Web pages for use elsewhere or to tidy up your HTML code to make faster loading Web pages. Detagger runs in two distinct modes: selective markup removal and full conversion to text files.

As a markup remover, Detagger allows you to tidy up your HTML code in a number of ways. You simply select classes of tags you want removed, sections of code you want stripped out, or tag manipulations you want performed. Options include the ability to remove all FONT tags; remove tags added by Microsoft Office applications; strip out HEAD, SCRIPT, or FORM sections; convert tags to uppercase or lowercase; and replace hyperlinks with their display values.

As an HTML-to-text converter, Detagger allows you to convert HTML newsletters into a more compact format. The program will output the document as text, preserving the marked-up headings, lists, tables of the original document, and turning them into suitable text formats. Text will be laid out as faithfully as possible to the original document, within the constraints of your chosen page width. Simple tables can also be converted into comma- or tab-delimited data, ready to import into spreadsheets.

Hyperlinks are replaced by their display text, and you can choose to have the link URL displayed in the text or added as an entry in a reference table at the end of the document. Images can be replaced by their ALT tags. There are many formatting options to lay out the text which allow you to specify the page width, justify the text, insert gaps between sentences, and to format dialog clearly. Selected options can be saved in policy files so that they may be easily reloaded in later sessions.

Cross-platform Data Exchange

EldoS Group has released MsgConnect 1.0, an open source, cross-platform framework that enables data exchange. MsgConnect encapsulates low-level transport protocols (currently TCP and memory-mapped files) and provides a uniform API for exchanging binary information on a single system or across a network.

MsgConnect utilizes the paradigm of messages and provides optional support of Rijndael encryption, ZLib compression, and CRC32/Adler32/MD5 integrity checking. The program is available for a wide variety of platforms and development tools. MsgConnect can be used in both open source and commercial applications.

Is that License Up To Date?

Aspose has released Aspose.License, which allows developers to license their applications, components, and controls built with the .NET Framework both for Windows® Forms and Web Forms. Aspose.License is a software licensing component for Microsoft .NET that can check against preset license fields including but not limited to product, company, version, license name, expired days, expired date, owner name, as well as your own custom license fields. With it, you can store and distribute your licensing strategy via multilevel encrypted license files while managing issued licenses via XML. The component is platform independent as well as database independent.

Programmer's Text Editor

Emurasoft Inc. has released EmEditor 3.31, the latest version of their text editing shareware tool designed mainly for Web and software development. EmEditor features an extensive toolset and an array of plug-ins for editing a variety of programming languages. New features include a Windows Toolbar to switch between windows and a Page Setup dialog box where details such as which printer to use, page size, and page orientation can be specified.

Another new function, Save/Restore Workspace commands, allows users to save and restore the positions of windows on the desktop. EmEditor fully supports Unicode, enabling users to edit in multiple languages. The program also utilizes an assortment of plug-ins in order to extend its functions for specific needs. EmEditor supports syntax highlighting for many programming languages and is compatible with Windows XP.

Web Content Management

Ektron recently rolled out updates of their browser-based content management products, Ektron CMS200 and Ektron CMS300. New features for version 3.1 of both products include support for Microsoft Active Directory® and load balancing functionality. The target customer creates and manages mid- and large-sized deployments.

Ektron CMS200 offers word processor-like editing (via Ektron eWebEditPro), a permission model, and multistep workflow. It also includes international language support, metadata support, an API for user-maintained navigation, stylesheet support, syndication, and a teaser display. Ektron CMS200 features administration much like Windows 2000 and an interface similar to Windows Explorer.

Ektron CMS300 offers a browser-based XML content management system. It encompasses all Ektron CMS200 features, plus capabilities that enable the creation of XML using Smart Web Forms. By combining Ektron CMS300 with XSLT and XML schemas, organizations can enforce content style and page layout, ensure content validity, and optimize content for reuse to various consumers or devices.

Managing Patches

RippleTech Inc. recently launched its PatchWorks patch management automation software for Windows. PatchWorks evaluates networked workstations and servers to identify security or stability vulnerabilities due to missing or out-of-date hotfixes or service packs. Once identified, PatchWorks remotely deploys the appropriate patches through its central management console and validates each installation to ensure that it was completed successfully.

PatchWorks' continuously updated database makes it easy to find the patches that are critical to securing and stabilizing business systems and applications. Each patch is verified and tested by experts for deployment interdependency issues, so patches can be installed with confidence. With PatchWorks' policy management and conformance reporting features, companies can establish and ensure adherence to update policies. Powered by St. Bernard Software, the product supports Windows NT® 4.0, Windows 2000, Windows XP, and many software applications, such as SQL Server™, Microsoft Exchange Server, Microsoft Internet Explorer, Microsoft Outlook, and more.

Share Session Variables

Westglenn Software Inc. has announced the release of SessionBridge, an application that allows ASP.NET and ASP pages that operate within the same application to share session variables seamlessly, while gradually converting those pages to ASP.NET. SessionBridge includes a COM object that intercepts calls to ASP session variables and stores them as ASP.NET session variables. The only changes needed to the ASP code can be handled with a global search and replace.

Since the ASP.NET session objects are used to store the variables, the ASP.NET code is exactly what you would normally write. When the entire application has been converted, you are no longer using SessionBridge and are left with standard ASP.NET code. SessionBridge allows you to migrate to ASP.NET gradually as an extension of your daily workflow.

Barcodes for the .NET Framework

The ASP.NET Barcode Server Control, recently published by IDAutomation.com, is a drag-and-drop component that adds dynamic barcoding capability to Web applications using the .NET Framework. This server-side component creates JPEG images, so all processing takes place on the server, making it compatible with all Web browsers. Online tasks that involve items printed from a Web browser, such as online tickets, coupons, name badges, invoices, registrations, rebate mailers, check-in confirmations, RMA packing slips, and gift certificates, can be automated with barcodes.

Online barcodes may be included in Web applications by dragging the control on a form in the same way a textbox control is used. Several barcode types are supported in a single control including Code 3 of 9, Code 128, UCC/EAN-128, Interleaved 2 of 5, UPC, EAN, Codabar, Planet, and Postnet. The Barcode Server Control also includes adjustable orientation, automatic sizing, automatic temp file maintenance, and high quality image creation.

Integrate with QuickBooks

AcctSync Technologies Inc. has announced the release of AcctSync SDK, a toolkit for QuickBooks developers and integrators. AcctSync SDK will enable QuickBooks developers to build integrated solutions using their preferred development environment such as the .NET Framework, C++, and others, without any prior knowledge of qbXML, XML, or COM APIs. AcctSync SDK is built on top of the qbXML SDK, Intuit's development kit that enables data integration between third-party applications and QuickBooks.

AcctSync SDK has no external dependencies, other than QuickBooks; users do not need to download the QuickBooks SDK, an XML parser, or any other utilities to run applications built with AcctSync SDK. Nearly all the components included in the AcctSync SDK correspond to the most commonly used QuickBooks constructs like Customer, Employee, Vendor, Invoice and so on, and can be used to store information and communicate with QuickBooks. AcctSync SDK is available for the .NET Framework.

Interactive HTML Help

A!K Research Labs released version 2.16 of chm2web, its online help system creation utility that offers the ability to create interactive Web-based help systems. The application is capable of creating integral help systems from existing .chm (compiled HTML help) projects. chm2Web is intended both for developers and Webmasters using the .chm file format for maintaining digests and online knowledge bases.

The application features a wizard interface which helps you create customizable template-based help systems that both comply with the current Web site design and utilize client-side scripting technologies to make the help system interactive. chm2Web runs under all versions of Windows after Windows 98.

The Bookshelf

Sybex Inc. has announced two recent additions to its family of books targeting the .NET Framework. Visual Basic .NET Developer's Handbook by Evangelos Petroutsos and Mark Ridgeway is a revised edition for experienced developers using Visual Basic. Readers will learn to use Visual Basic .NET to build and deploy XML-based Web Services, create Web applications in ASP.NET, and implement support for mobile devices. The book also covers trickier aspects of form design, solving tough database connectivity challenges, and securing your applications with built-in features of the .NET Framework.

.NET Framework Solutions: In Search of the Lost Win32 API by John Paul Mueller is filled with undocumented techniques for duplicating Win32® API functionality within the .NET Framework. The topics covered are as varied as the missing functionality: direct hardware access, low-level security control, certain aspects of OS access, support for multimedia and utilities, and DirectX®. You also get hard-to-find information on COM access, plus a collection of examples—dealing with DirectX and the MMC snap-ins—that unite COM and Win32 access. Find out more at http://www.sybex.com.

ASP.NET Kick Start, by Stephen Walther, was recently released by SAMS Publishing. The book teaches developers how to use Visual Studio® .NET to build ASP.NET applications. Walther shares tips and tricks to reduce development time using Visual Studio .NET. Each chapter is a self-paced tutorial that enables readers to immediately start building ASP.NET applications. This book concentrates on the tools and features built into Visual Studio .NET.

SAMS has also released ASP.NET Data Web Controls Kick Start by Scott Mitchell. This book explains how to dig into advanced features of the Data Web controls. These controls are fairly simple to use when it comes to simple data display. However, they can do much more than merely present data. For example, the DataGrid Web control allows for sorting, paging, and editing of data. In addition, these controls allow for templating of data, which can be used to provide more complex views. Check out the details at http://www.samspublishing.com.

Contacts

A!K Research Labs http://www.chm2web.com

AcctSync Technologies Inc. P.O. Box 13821, Research Triangle Park, NC 27709, 800-225-4190, 919-402-0590, http://www.acctsync.com

Aspose Pty Ltd. Sydney, Australia, http://www.aspose.com

Consonica Ltd. London, UK, +44-8709-220-638, http://www.consonica.com

Ektron 5 Northern Boulevard, Buidling. 6, Amherst, NH 03031, 603-594-0249, http://www.ektron.com

EldoS Group Chokolovski Boulevard 10, r.57, 03186, Kiev, Ukraine, +380(44)-202-8439, http://www.msgconnect.com

Emurasoft Inc. 8638 154th Avenue NE, Redmond, WA 98052, 425-882-9988, http://www.emurasoft.com

Graphics Server Technologies P.O. Box 4545, Seattle, WA 98104, 206-625-6900, http://www.graphicsserver.com

IDAutomation.com P.O. Box 1816, Issaquah, WA 98027, 877-353-7297, 952-646-5331, http://www.idautomation.com

JafSoft Limited 35 Nursery Road, Heaton Norris, Stockport, Cheshire, SK4 2ND UK, +44 (161) 718 0211, http://www.jafsoft.com/detagger

RippleTech Inc. 500 South Gravers Road, Suite 2000, Plymouth Meeting, PA 19462, 610-832-1100, http://www.rippletech.com

Westglenn Software Inc. 2100 SouthBridge Parkway, Suite 260, Birmingham, AL 35209, 800-711-5153, 205-423-0100, http://www.westglenn.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: Storing SQL Data, URL Query Length, and More

Web Q&A
Storing SQL Data, URL Query Length, and More
Edited by Nancy Michell

Q What's the best way to store large amounts of XML data in SQL Server™? What are the performance implications of storing it in large chunks versus breaking it out into tables?

A Different criteria play a role in that decision. If the data in the XML document is highly structured and fits into a relational model, it is often queried on a granular level, and you rarely need to get the XML back into its original form (in other words, order does not matter). In this case, decomposition into columnar data is better. If you have more document-oriented XML where order matters and recomposition costs are high, a Character Large Object or XML datatype-like approach is better.

Q When I send an XML query to SQL Server in a URL, after its length goes over about 1000 characters, I get the following error. Is it a bug?

<H3>ERROR: 400.100 Bad Request</H3><b>HResult:</b>
0x80004005<br><b>Source:</b> Microsoft SQL isapi
extension<br><b>Description:</b> The URL exceeds maximum length.<br>

A This limitation is by design. It was introduced as part of the security push for SQLXML 3.0 SP1. The length of the query part of the URL is limited to 1024 characters. If you want to send more information to this virtual directory, you need to use HTTP POST.

If you are using POST, then the SQLXML ISAPI limits the amount of data that can be posted to this maximum value. It's up to you to choose the right value for your scenarios. You can still post large query requests using POST, but not through the URL. POST size is not unlimited, but it's configurable. What's specified as the maximum size of POST queries still applies. You can set it in Microsoft® Internet Information Services (IIS), as shown in Figure 1. Using ServerXmlHTTP should allow you to post larger data.

Figure 1 Maximum POST Size
Figure 1 Maximum POST Size

Q Is there a maximum number of classes that can be used in a single Visual Basic® 6.0 project?

A There is, but it's not a fixed number. As stated in Visual Basic 6.0 Programmer's Guide (Microsoft Press, 1998), the module entries table accepts up to 125 bytes per module, with a total limit of 64KB, resulting in about 400 modules per project. This includes classes, forms, and modules.

If you exceed this, you'll get a strange error. Your only hope is to reduce the number of classes by splitting your project into parts, combining the functionality of multiple classes into one, or upgrading to Visual Basic .NET.

Q I have the following class:

Public Type MyType
    SimpleValue As Integer
End Type
Private pTypeValue As MyType

Public Property Get MyTypeValue() As MyType
    MyTypeValue = pTypeValue
End Property

Public Property Let MyTypeValue(ByRef Value As MyType)
    pTypeValue = Value
End Property
The code using this class looks like the following:
Dim MyObject As Class1
Set MyObject = New Class1

Dim x As MyType
x.SimpleValue = 1
MyObject.MyTypeValue = x
MsgBox MyObject.MyTypeValue.SimpleValue
I need to know if I can assign only a single value to "MyTypeValue", like this:
MyObject.MyTypeValue.SimpleValue = 7

A Well, you can assign it, but it doesn't actually accomplish anything. MyObject.MyTypeValue returns a copy of the internal structure, and you're making a change to the copy, which will be thrown away as soon as the line of code completes execution.

Enabling you to access a structure that is internal to a class instance is inherently unsafe because the class can be destroyed, along with the memory for the structure. If you want to do this, though, you have two options. You can make MyType into a class or you can get a copy of the structure, edit it, and assign it back into the class as an atomic unit when you're finished with it.

Note that the copy in/copy out is not very expensive unless you're moving strings and arrays, in which case you can end up making deep copies of large amounts of data.

Q Is there a way for an ActiveX® control to detect if it is embedded in a Visual Basic 6.0 form? I need a public interface that the control can query using QueryInterface on the client site that hosts it.

A For a control created in Visual Basic, the following will work:

TypeOf UserControl.Extender Is VBControlExtender
For a control not built in Visual Basic, you can get the extender object by following these steps:
  1. Call IOleObject::GetClientSite
  2. Query the client site for IOleControlSite using QueryInterface
  3. Query the GetExtendedControl method on the control site using QueryInterface
  4. Query the extended control for VBControlExtender (IID={164CBDD0-7321-11D1-A1E8-00A0C90F2731}) using QueryInterface

Q How can I tell what language an operating system is running in?

A GetSystemDefaultLCID is very helpful for systems running Windows® 9x (where the setting cannot be changed), but does not do much for Windows NT®, where it can be changed. To achieve this on Windows NT you will need to check the language version of specific files such as kernel32.dll. On Windows 2000 or Windows XP, you can use the GetUserDefaultUILanguage API. You can get more info on this at http://www.microsoft.com/globaldev.

Q Is JScript® on a DHTML page executed in a single thread? What about cross-frame function invocation? If not, what should I do if I need to have a critical section in a JScript function?

A All script code for a window is executed from a single thread. You don't have to worry about cross-frame calls in a single window. You do have to be careful if you are making calls between windows. The JScript code is executed in a single-threaded apartment thread and therefore you'll never need a critical section in the classical sense. However, you do need to be concerned because whenever a callout is made from one window, a call-in can occur from a secondary window. Callouts can happen pretty much on any line of JScript code because they will occur if the garbage collector runs and releases a proxy to an object on a different thread.

Q I want to open Microsoft Internet Explorer with a specific Web site already loaded. What is the best way to do this?

A Try this:

Set oShell = CreateObject("Wscript.Shell")
oShell.Exec ("C:\Program Files\Internet Explorer\IEXPLORE.EXE
http://www.msn.com")

Or better yet, add a little error handling, and find the local hard drive location. Not everyone installs their operating system to the C drive, so hard coding C is generally not a good idea (see Figure 2).

Figure 2 Find the Drive (Close Figure 2)
Set oShell = CreateObject("Wscript.Shell")
Set FSO = CreateObject("Scripting.FileSystemObject")

ProgramFiles = oShell.ExpandEnvironmentStrings("%ProgramFiles%")

IEPath = ProgramFiles & "\Internet Explorer\IEXPLORE.EXE"

if FSO.FileExists(IEPath) then

            oShell.Exec (IEPath & " http://www.msn.com")
else
            Wscript.Echo "Unable to find Internet Explorer executable."
            Wscript.quit 2989
end if

You could avoid calling iexplore.exe in a shell altogether:

Set IeWindow = wscript.CreateObject("InternetExplorer.Application")
IeWindow.Navigate "http://www.msn.com"
IeWindow.Visible = 1
Finally, you could use the following statement:
Set oShell = CreateObject("Wscript.Shell")
oShell.run ("http://www.msn.com")

Q I recently inherited a project which contains JScript and I really need to learn how to debug it. The project is written in C++ and JScript, in both .htm and .js format.

I installed Visual Studio® .NET because it has a script debugger. I opened up the .exe and one of the .htm files that I'd like to debug. I set a breakpoint on a line that I know gets hit when I run the program, but when I start the program the breakpoint symbol has a "?" in it and the tooltip says that in "TryWebService.htm, line 10667 character 31 the breakpoint will not currently be hit. No executable code is currently loaded at this location."

However, I know that part of the JScript is indeed getting executed. How can I set a valid breakpoint?

A In the properties of the project you're debugging there will be a number of checkboxes for managed code, unmanaged code, and script code—you need to check which kind you are intending to debug. Here's a few links that may help you with the script debugging: Debug Client-Side Script in Visual Basic .NET, and BUG: Debugger Skips Client-Side Breakpoints in ASP.NET Projects. Most of the information in these links is about ASP.NET projects, but the sections on client-side debugging should be helpful.

Q I need to know how to free the memory allocated by the array in JScript created here:

<%@language="JSCRIPT"%>
<%
var m_aTmp = new Array();
for(var idxArr=0;idxArr<=1000;idxArr++)
m_aTmp[idxArr] = "TESTING\n" ;
%>
<%=m_aTmp%>

A JScript has garbage collection, but you can also assign a null value or an empty string and the object will get cleaned up:

for (i = 0; i <= 1000;i++)
{
    m_aTmp[i] = "";
}//end for

Also, after the new allocation you can force the garbage collection to run immediately with the undocumented CollectGarbage function, which was implemented mainly for debugging purposes and can be a great help when debugging script/COM objects.

Don't use any undocumented API ( | Method | Function | ...) outside of debugging or in release products. Also note that calling CollectGarbage does not guarantee that all memory that can be freed is released. A garbage collection may trigger destructors, which then cause more memory to become collectable.

JScript is an automatic storage language. If you care about exactly when objects are released, you should be using a language such as C, which allows you to precisely control object lifetime.

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

Thanks to Paul Andrew, Earl Beaman, Sébastien Bovo, Marc Cauchy, Jeffrey Cline, Matt Curland, Edson Dos Santos, Jin Feng, Eunyoung Go, Madhuvan Gupta, Doug Hettinger, Harish Jayakumar, Julia Jia, Eric Lippert, Srinivas Somu, Maoni Stephens, Matteo Teruzzi, Michael Woodland, Rok Yu


Data Points: Techniques for Managing Rowset Paging

Data Points
Techniques for Managing Rowset Paging
John Papa

Code download available at: DataPoints0305.exe (110KB)
Contents
Client-side Options
ASP State
State Management in ASP.NET
Paging in SQL
Conclusion

There are a number of ways to handle paging in Web applications. This month I'll examine several paging techniques and weigh their pros and cons. I'll discuss and demonstrate how to manage paging and caching issues through the lower tiers of an n-tiered architecture, including how to make SQL Server manage the paging on your app's behalf.

Most Web commerce sites have at least one page that lists rows from a rowset. Usually the rowset lists such things as the results of a search or the contents of a shopping cart. One common dilemma is how to handle the paging of a rowset on a Web page when the rowset could contain 1 or 100,000 rows.

In addition, one of the key decisions you need to make in deciding such things as how to handle paging in a Web application is where in the architecture the paging process belongs. You could choose to manage the paging of a rowset in the database, in the business component, in ASP, or even in the client. I'll walk through the advantages and disadvantages of each of these choices. I'll take the top-down approach in this column and start out at the presentation tier of the n-tiered architecture, where I'll discuss the issues involved in the management of paging at the client level using various caching features from XML data islands through DHTML. Then I'll discuss paging found in the middle tiers, such as in ASP using caching techniques like the ASP Session object. I'll also discuss how to manage paging of rowsets using the database's tools; in particular, stored procedures (SP) and table datatypes. Finally, I'll review the options and present some food for thought on when to choose which option for your application.

Client-side Options

Displaying a rowset and managing the paging through client-side code in the browser can be accomplished in a few different ways. But first you must consider the browser that your application will support. If you want your application to run in both Microsoft® Internet Explorer and Netscape, you'll have fewer options due to the differences in the browsers' Document Object Models (DOMs). One technique for managing paging in the browser is to pass the data to the browser and store it there, perhaps in an XML data island. However, if you are using Netscape browsers as well, you may consider storing the data in the browser using JavaScript objects instead.

If you are running in a LAN environment and you want to avoid round-trips to the server, you could pass the data to the client and store it there. That way, when you go to the next page of the rowset, you have the data with you and you can use some DHTML to load the next page. For example, let's say the rowset contains a list of 100 customers and your application passes it to the client. The browser could then store it in an XML data island and you could use some DHTML code to display the first 10 records. Then when the "next page" hyperlink is clicked, you could call a function that clears the records that are being displayed and loads the next 10 records in the XML data island into an HTML table.

One drawback to this approach is that you are sending a lot of data to the browser and relying on the browser's capabilities. Another drawback is that all of your data is being cached (in any caching scenario), which means that it is insensitive to changes being made in the database. The upside is that you can page through the data without having to make round-trips to the server. While this scenario is viable, it is not the optimal solution when compared to managing the paging in one of the lower tiers. It could destroy an app's performance if the rowset is large enough and the bandwidth is low.

ASP State

Managing the paging of a rowset through ASP state can be accomplished by using some sort of state management technique. In order to cache a rowset using ASP, the rowset would need to be stored in a database, the ASP Session object, a file, or by some other state persistence mechanism. Storing a rowset in a file, perhaps using XML, would allow the application to persist the rowset, but would mean that the application might have a significant file system bottleneck, as I/O access is not as efficient as database access or Session access. Plus, persisting the data to a file requires a naming convention that is unique to the active user's session, and that data would have to be cleaned up by some process.

Storing the rowset in the ASP Session object is often a better option than persisting to a file—the trick is choosing the container for that data. Storing an ADO Recordset object in an ASP Session object is not scalable because of the way the ASP Session stores objects. However, storing the data in an XML format in the Session object yields more promise. This way, you do not have to be concerned with storing an object in Session, since the XML can be stored as a string. Plus, you can easily go from XML to an ADO Recordset and back if need be. However, the cost of storing data of any kind in the ASP Session object can add up. When you have a tremendous amount of users hitting your Web application, all of the data stored in Session can hog your server's memory. Also, the ASP Session object is not scalable across load-balanced servers in a Web farm. For these reasons, using the Session object to cache rowsets should be considered carefully before committing to this solution. In situations where you need to get to the data and must cache at the ASP level, this is an acceptable solution but there are problems when scaling to more simultaneous users. Also, keep in mind that the ASP Session object is stored in the Web server's process, so if the Web service stops, the state is lost.

Another option when caching rowsets using ASP is to cache the data in a state database. Unlike ASP.NET, ASP 3.0 does not have this built-in feature, but it's easy to implement in any case (see "Taming the Stateless Beast: Managing Session State Across Servers on a Web Farm" in the October 2000 issue of MSDN® Magazine for one such solution). Storing the data in a database is a more scalable solution than using the ASP Session object because it does not have the memory limitations that the ASP Session does. Also, state databases can be used in a Web farm. However, accessing data stored in a database is less efficient than accessing data stored in memory.

As you can see, when caching rowsets in ASP there are considerations to make when choosing the option that best fits your needs. State databases are generally more scalable, while ASP Session objects are simpler to implement and faster. In building enterprise solutions, I often suggest using a state database because the performance you give up is usually negligible when managed properly.

State Management in ASP.NET

As mentioned previously, ASP.NET has more built-in state management options than ASP 3.0. ASP.NET offers three solutions: in-process state (similar to the traditional ASP Session), out-of-process state in a state service, and an option that uses a state database. The fastest option is still using in-process state. However, the state service and state database options in ASP.NET offer more resiliency and scalability. The ASP.NET state service uses memory, just like the in-process state; however, it maintains the state outside of the Web server's process space. So when using the ASP.NET state service your state will survive any Web service restarts. Of course, state stored in a state database will also survive any Web service restarts, but it will also survive Web server reboots. One of the greatest benefits of ASP.NET state is that you can switch between the different management techniques by changing the sessionState mode setting in the config.web file, as shown here:

<sessionState
   mode="Off|Inproc|StateServer|SqlServer"
   cookieless="true|false"
   timeout="number of minutes"
   connectionString="server name:port number"
   sqlConnectionString="sql connection string" />
Regardless of which mode you use in ASP.NET, all of the code to access and store state throughout your application is unaffected.

Paging in SQL

One common problem with all of the caching techniques discussed thus far is that they all require that the entire rowset be cached somewhere. This means that if you had a rowset with 1,000 rows of data, all 1,000 rows would be cached in an XML data island, the ASP Session object, a database, or whatever caching method you choose. It also means that you are transporting all 1,000 rows across the network from the database to the Web server on the initial call. If you want to limit what you transport across the network and do not want to cache the entire rowset, consider using the database to do the dirty work or think about finding some other custom paging technique.

One key feature of relational databases is that they are good at managing data. So why not ask the database to retrieve only the rows that you want? For example, if a rowset has 1,000 rows and you want to get the first 10 through a stored procedure, you could query the database for just those 10 records. When the user moves to the next page of the rowset, you would then ask the database to retrieve rows 11 through 20. While this technique requires more interaction with the database, you are not moving all that data across the network, nor are you caching it anywhere. One upside (or downside depending on how you look at it) is that your application would always show the latest database contents. However, this could also result in skipped rows when you page through.

One way to do this is to create a stored procedure that accepts the beginning and ending row numbers that you want. Figure 1 shows a sample stored procedure that, when run against the Northwind database, will return only the rows requested from the orders table. Of course, this stored procedure could be modified to narrow the search to orders for a particular customer or in a certain date range.

Figure 1 Paging Using a Stored Procedure (Close Figure 1)
CREATE PROCEDURE prPagingExample
    @nRecordNumberStart INT,
    @nRecordNumberEnd INT
AS
    SET NOCOUNT ON

    --------------------------------------------------------------------
    -- Define the table to do the filtering and paging
    --------------------------------------------------------------------
    DECLARE @tblTempData TABLE
        (
        nID INT IDENTITY,
        OrderID INT,
        OrderDate DATETIME,
        CustomerID NCHAR(5)
        )

    INSERT INTO @tblTempData
        (
        OrderID,
        OrderDate,
        CustomerID
        )
    SELECT
        OrderID,
        OrderDate,
        CustomerID
    FROM Orders
    ORDER BY
        CustomerID,
        OrderDate

    SELECT     OrderID,
        OrderDate,
        CustomerID
    FROM     @tblTempData
    WHERE    nID BETWEEN @nRecordNumberStart AND @nRecordNumberEnd
    ORDER BY
        nID ASC

The stored procedure in Figure 1 first creates a variable called @tblTempData of the TABLE datatype. This table contains all of the fields that you want to retrieve from the query plus an additional column called nID. The nID column is an integer and is used to number each of the rows. As the rows are inserted into the table @tblTempData, they are numbered from 1 to n (where n is the number of rows). Since the nID column is typed as an IDENTITY value, the row numbering is created and stored in this column. Once the rowset is numbered, retrieving a specific range of rows becomes easy. The last part of the SP retrieves all of the columns but nID from the table @tblTempData, limiting the rows where the nID is within the range that was passed in.

This option does not require caching of the rowset in ASP or on the client, nor does it place a heavy burden on the network since the rowset is limited to a reasonable size before it leaves the database. However, this option does retrieve all of the data in the stored procedure each time and then discards most of it before returning the rowset. Also, since this stored procedure may be called repeatedly, if the query takes a long time to run, it could severely impact the performance of your application. If the query is slow, it would be better to use a caching option such as storing the rowset in a state database through ASP 3.0 or ASP.NET.

Conclusion

Some other simple but effective ways of enhancing paging performance are to limit the number of rows that a user can see on a page (don't allow the user to show more than 500 rows at a time) and optimize your searching query as much as possible. If your application allows the user to search on 10 different fields, it would be better to run a SQL query that only operates against the fields that the user is interested in. For example, if the user searches for all orders within a certain date range, your WHERE clause should include this criteria. You could create a single stored procedure that searches all of the fields at one time, which would be easy to maintain, but you will get better performance by writing a SQL™ statement that dynamically constructs the WHERE clause using only the criteria needed. It is a little harder to maintain, but that is easily made up by gains in performance.

What becomes pretty clear after some research into paging and caching techniques is that there is no perfect solution free of side effects. Caching on the client is the fastest of all of the solutions once the page is loaded. However, the initial loading of the page takes more time and requires more client-side code to manage the paging. While there are several techniques for caching data in ASP, the best option from my experience when using ASP to store a rowset in the XML format is to use a state database. Whether using the one that comes with ASP.NET or building your own, the performance is good and the data is resilient.

When caching at the ASP level is not an option for you, consider using a stored procedure to page the data, as shown in Figure 1. Using the stored procedure yields good performance (without caching) as long as the query does not take excessive amounts of time to execute. There are other options for paging and caching rowsets as well, including the use of the shared property manager of Microsoft Transaction Services to cache the data in a business object and other custom techniques.

Whatever option you end up using, it is important to make sure that the solution performs well for your application, is easily maintainable, and can scale with your application. When choosing a paging technique, take care to weigh your options against your application's context.

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

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


Cutting Edge: Form-based Programming in ASP.NET

Cutting Edge
Form-based Programming in ASP.NET
Dino Esposito

Code download available at: CuttingEdge0305.exe (117KB)
Contents
Reasons for a Single-form Model
The HtmlForm Class
Working with Forms in ASP.NET
Multiple Form Tags
Using User Controls
Using a Custom Form Control
Popup Forms
A Quick Look at Mobile Controls

One of the most common snags that ASP developers encounter when they first approach ASP.NET is that managed Web applications must be written according to a single-form interface model. I'll call this model SFI. Don't panic because you've never heard of SFI. I invented it for the sake of convenience to align with other acronyms that describe similar programming models—the single-document interface (SDI) and the multiple-document interface (MDI). I hope this will make the concept a little easier to grasp as you read through this column.

In the SFI model each page always posts to itself by default using the POST HTTP method. The HTTP method and the target frame of the post can be programmatically adjusted using ad hoc form properties—Method and Target. The final destination of the postback can't be changed in any way. In HTML and ASP programming, the form element features the Action property that programmers use to redirect to another URL after clicking. In ASP.NET, server-side forms are rendered using a class named HtmlForm. This class doesn't provide the familiar Action property, nor does it supply an alternative property. As a result, the SFI model is so entrenched in ASP.NET that there's no way around it.

In this column, I'll review programming forms in ASP.NET and discuss ways to implement a multi-form programming model. In doing so, I'll also take a look at the details of the multi-form model available for ASP.NET mobile controls.

Reasons for a Single-form Model

Technically speaking, in ASP.NET a kind of multi-form interface (MFI—again, my acronym) is still possible but requires a number of tricks. By design, an ASP.NET page can host exactly one server form. As I'll demonstrate later, though, you can add any number of HTML <form> tags without the runat attribute.

If you place two or more server forms in a page, you won't get any error at compile time. The request for the page is therefore processed as usual using the code built in the Page base class. The temporary class for the page is generated, compiled, and finally loaded. The ASP.NET runtime begins processing the page request and regularly fires the Init, Load, and PreRender events. All these events are handled seamlessly. After the events are handled, the page enters its rendering phase.

The class that represents the page calls the Render method on all the individual controls included in the page. So the first <form> block is successfully processed and rendered. After that, the page object sets an internal flag to note that one form object has been processed for the page. Can you guess what happens next?

When a <form runat="server"> block is found, the page first verifies that no similar block has already been processed. If the aforementioned internal flag is set, an HttpException is thrown. More precisely, the page loops through its child controls and asks them to render. HtmlForm classes are no exception. So the following methods are called in sequence:

Page.RenderControl
    •••
HtmlForm.Render
HtmlForm.RenderChildren
RenderChildren is a protected virtual method defined on the class HtmlForm. It renders the form's child controls within two calls to internal methods on the Page object, as you can see in the following pseudocode:
protected virtual void RenderChildren(HtmlTextWriter writer)
{
   Page.OnFormRender(writer, ClientID);
   base.RenderChildren(writer);
   Page.OnFormPostRender(writer, ClientID);
}

OnFormRender and OnFormPostRender are internal methods of the Page class that control the rendering of the forms. In particular, the first method sets a Boolean variable that is responsible for throwing the exception (see Figure 1). The following pseudocode illustrates the structure of the OnFormRender method:

void OnFormRender(HtmlTextWriter writer, string formID)
{
   if (serverFormCreated)
    throw new HttpException("Multiple_forms_not_allowed");
   serverFormCreated = true;
   •••
}

Figure 1 Error
Figure 1 Error

What does this mean to you? As of ASP.NET 1.1, there's no compile-time control on multiple server-side forms. You could include as many server-side forms as you need in a Web Forms page, as long as you ensure that no more than one is visible at any time. In practice, if you set the Visible attribute of additional server-side forms to false, the page will render just fine. Of course, you can control the Visible attribute of forms programmatically and decide which one to show based on run-time conditions. Incidentally, a similar approach is comparable to that of ASP.NET mobile forms in which multiple server-side forms can be defined but only one form at a time is visible. The code in Figure 2 shows a trivial ASP.NET page with multiple, but mutually exclusive, forms.

Figure 2 Multiple Forms (Close Figure 2)
<%@ Page Language="C#" %>
<script runat="server">
</script>

<html>
<body>

<form runat="server" id="First">
   First form
</form>
<form runat="server" id="Second" visible="false">
   Second form
</form>
<form runat="server" id="Third" visible="false">
   Third form
</form>

</body>
</html>

The single-form model is a key element for the implementation of the ASP.NET state maintenance feature. Since the page always posts to itself, the runtime finds it easy to retrieve and restore the state saved during the last access. While there are other ways to save and restore the state of a page, using a single-form model is by far the simplest and most effective of all solutions.

In general, renouncing multiple forms is not a big sacrifice. Multiple forms are sometimes useful, but probably not as frequently as you may think. A scenario in which you would find it helpful to use multiple forms is when you implement form-based functionality in a large number of pages—in search and login dialogs, for example. I'll return to this later in the column. Before going any further, though, a review of the HtmlForm class is in order.

The HtmlForm Class

The HtmlForm class inherits from HtmlContainerControl and implements the IAttributeAccessor interface. The base class provides HtmlForm with the ability to contain child controls. This capability is shared with other HTML control classes, such as HtmlTable, which is characterized by child elements and a closing tag. The IAttributeAccessor interface defines two methods, GetAttribute and SetAttribute, which are used to read attribute values out of the opening tag.

The HtmlForm class provides programmatic access to the HTML <form> element on the server through the set of properties listed in Figure 3. The form must have a unique name. If the programmer doesn't assign the name, ASP.NET generates one using a built-in algorithm. The default name follows the pattern _ctlX, in which X is a unique integer—typically the ordinal position of the control in the page. The programmer can set the form's identifier either using the ID or the Name property. If both are set, the ID attribute takes precedence. Based on the value of ID, ASP.NET determines the values of UniqueID and ClientID. UniqueID is a fully qualified string based on the naming container of the form. ClientID, on the other hand, is used to identify a control for client-side operations, such as JavaScript functions. It is worth noting, however, that this logic applies to all server controls, not just forms.

Figure 3 HTMLForm Properties (Close Figure 3)

Property | Description
Attributes | Returns a name/value collection with all the attributes declared on the tag
ClientID | Returns the value of UniqueID
Controls | Gets a collection object that represents the child controls of the form
Disabled | Gets or sets a value indicating whether the form is disabled; match the disabled HTML attribute
EnableViewState | Rarely used at this level; gets or sets a value indicating whether the state of child controls should be persisted
Enctype | Gets or sets the encoding type; match the enctype HTML attribute
ID | Gets or sets the programmatic identifier of the form
InnerHtml | Gets or sets the markup content found between the opening and closing tags of the form
InnerText | Gets or sets the text between the opening and closing tags of the form
Method | Indicates how a browser posts form data to the server; the default value is POST; can be set to GET if needed
Name | Returns the value of UniqueID
NamingContainer | Gets a reference to the form's naming container, typically the host page
Page | Gets a reference to the host page
Parent | Gets a reference to the parent object; the parent object is typically, but not necessarily, the page
Site | Returns null
Style | Gets a collection of all cascading style sheet properties applied to the form
TagName | Returns "form"
Target | Gets or sets the name of the frame or window to render the HTML generated for the page
TemplateSourceDirectory | Gets the virtual directory of the page that contains the form
UniqueID | Gets the unique, fully qualified name of the form
Visible | If false, the form is not rendered to HTML

The parent object of the form is the innermost container control with the runat attribute. If such a control doesn't exist, the page object itself is set as the parent. Typical containers for the server form are the <table> and <div> elements, if they're marked runat=server.

By default, the Method property is set to POST. The value of the property can be modified programmatically. If the form is posted through the GET method, all of the form data is passed on the URL's query string. However, if you choose the GET method remember that the limited size of a GET request can truncate your data if you're not careful.

Figure 4 lists the methods available on the HtmlForm class. All of these methods are inherited from the base System.Web.UI.Control class. You should note that the FindControl method only searches in the form's naming container. Controls belonging to an inner naming container (for example, a user control or a DataGrid control) are not found.

Figure 4 HTMLForm Methods (Close Figure 4)

Method | Description
DataBind | Calls the DataBind method on all controls within the form
Dispose | Performs the final cleanup before the form is freed
FindControl | Retrieves and returns the child control that matches the given ID
HasControls | Indicates whether the form contains any child controls
RenderControl | Outputs the HTML code for the form; if tracing is enabled, caches tracing information to be rendered later at the end of the page
ResolveUrl | Ensures that the specified URL is absolute; reads the base URL from the TemplateSourceDirectory property

The HtmlForm class is not sealed and thus can be further inherited. It also features a bunch of protected, overridable methods that you could exploit to customize the overall behavior.

Working with Forms in ASP.NET

Some pages need to have multiple logical forms. A logical form is not necessarily what you think of when you think of an HTML form. It is simply a logically related group of input controls whose contents can be submitted all at once to a piece of code that can handle it—a group of controls that, taken together, act like a form. Using a <form> tag to unite these controls is the simplest way to make a logical form. ASP.NET doesn't let you use multiple server-side <form> tags. However, you can use alternative ways to implement a logical form.

For example, consider a login page designated for restricted areas of a Web site. Registered users just type in their user name and password and connect. Unregistered users need to fill out a form before they're given the parameters to connect. How do you code this? Let's review a few options.

Figure 5 HTML Forms Posting
Figure 5 HTML Forms Posting

In ASP, you can use two HTML forms, each posting to a different page. The schema is roughly outlined in Figure 5. This solution is impracticable in ASP.NET if you're not using plain HTML tags (tags without the runat attribute) and an ASP programming style. (More on this in a moment.) The solution depicted in Figure 6 is better suited to ASP.NET.

Figure 6 Better Posting Solution
Figure 6 Better Posting Solution

One physical <form> tag contains multiple logical forms. All logical forms post to the same page and hide any unneeded user interface element. A unique page is involved and contains a single <form> tag. Within the opening and closing tags of the form, there can be two or more groups of logically related controls. Each group can post back either using a classic submit button or a hyperlink bound to some script code—what is known as a link button in ASP.NET. Although the various groups are not forms in the technical sense of the word, they do behave like HTML forms. All groups post to the same page, but the ASP.NET infrastructure guarantees both state maintenance and the execution of a piece of code specific to the group that caused the post. Each link button is associated with a server-side method that runs when the page posts back after the user clicks.

Functionally speaking, the mechanism is in no way different from the ASP implementation. Furthermore, the programming model enjoys a higher abstraction layer and handy features such as view state and server-side control programming.

How can you update the user interface of the HTML being generated? ASP.NET server controls can be hidden from view or restored programmatically. Controls with the Visible property set to false are ignored by the ASP.NET parser and no HTML code will ever be generated for them. Looking at Figure 6, you can design your login page to contain three blocks of HTML code: the login form, the registration form, and the restricted area. The first time you show the page—that is, when IsPostBack is set to false—the login and register blocks are displayed while the restricted area remains invisible and is completely ignored in the rendering phase. Next, the user clicks to either log in or register. The page posts back to execute code. When registering the new user, or checking the credentials of a known user, you simply toggle off the visibility of the forms and toggle on that of the restricted area.

The code in Figure 7 contains a form with two logical subforms, one with a button and one with the typical login fields. Both are wrapped by a server-side <div> panel. When the page displays the first time, only the first pseudo-form is visible. When the button is clicked, the RunDlg block is hidden and the other, named Login, is displayed. Keep in mind that when the Visible attribute is set to false, no code is generated for controls.

Figure 7 Two Logical Subforms (Close Figure 7)
<%@ Page Language="C#" %>

<script runat="server">
void OnShowLogin(object sender, EventArgs e) {
    RunDlg.Visible = false;
    Login.Visible = true;
}
void OnLogIn(object sender, EventArgs e) {
    RunDlg.Visible = true;
    Login.Visible = false;
}
</script>

<html>
<body>
<form runat="server">
    <div runat="server" id="RunDlg">
        Click here to login. <br>
        <asp:button runat="server" text="Login" onclick="OnShowLogin" />
    </div>

    <div runat="server" id="Login" visible="false">
        <hr>
        <b>Login</b><br>
        <asp:textbox runat="server" id="UserName" /><br>
        <asp:textbox runat="server" id="Pswd" textmode="password" /><br>
        <asp:button runat="server" text="Log in" onclick="OnLogIn" />
        <hr>
    </div>
</form>
</body>
</html>

Using this approach, you have a single page that contains a few logical forms and only one form is displayed at any time. It goes without saying that this approach can be extended a little further to execute code on an external page. So far, I assumed that the code to process the contents of the logical form was embedded in the host page. However, nothing really prevents you from transferring the control to another page, thus simulating an HTML form. For a better implementation of the solution that is shown in Figure 7, you can take advantage of user controls in order to wrap the contents of server-side <div> blocks.

In general, if it seems that there's no other way to code than using multiple forms, then you should probably rethink the code. By abstracting things a little bit, you can have an equally effective ASP.NET page with a single form and a more object-oriented design. In ASP.NET, think in terms of classes and functionality rather than in terms of HTML forms. However, especially if you're engaged in the migration of a complex Web site (such as a portal), resorting to multiple forms might be the fastest way to be not only on time but also on budget.

In many cases, you can make better use of the power of ASP.NET by redesigning the application so that you don't need multiple HTML forms. If the forms just point to completely different pages (search pages or mail pages) you can consider extensions to the ASP.NET model to simulate multiple forms, as in ASP, but without losing the state maintenance facilities of ASP.NET.

Multiple Form Tags

Having multiple HTML forms in an ASP.NET page is not a problem as long as you don't require automatic state maintenance. Even though multiple forms are employed in an ASP.NET page, only one of them can have the runat attribute set to server. Subsequently, ASP.NET can only provide view state management and postback events for the controls contained on that form. Let's consider the code for a Web Forms page shown in Figure 8.

Figure 8 Web Forms Page (Close Figure 8)
<!-- This is test.aspx -->
<html>
<body>
    <form runat="server">
        <asp:textbox runat="server" id="theTextBox" />
        <asp:button runat="server" text="Post back" />
    </form>

    <form method="post" action="another.asp">
        <input type="text" name="AnotherField" />
        <input type="submit" id="submit1" value="another.asp(x)" />
    </form>

    <form method="post" action="test.aspx">
        <input type="text" name="ThirdField" />
        <input type="submit" id="submit2" value="This page" />
    </form>
</body>
</html>

As you can see, the second and the third <form> tags are not marked with runat="server", meaning that the ASP.NET runtime will treat them as plain text to send out as is. When you click on the button named submit1, the browser navigates to a page named another.aspx. It is a brand new ASP.NET page that knows nothing about the main page's controls. You can still retrieve values from the referrer page, but only using the old ASP programming style. You retrieve only the values from the form that caused the post.

<%@ Page Language="C#" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
    Response.Write(Request.Form["SecondField"]);
}
</script>

ASP.NET ignores the controls defined outside the first server-side form of the page. When you click on the submit button associated with the server form, the page posts back but the content of any other textboxes is cleared. The same side effect occurs when you submit form's data using the third button. It is the submit button of an HTML form that posts to the same page—test.aspx. Posting to the current page is exactly what ASP.NET does, but why doesn't it work as usual if you command the post from within a client-side HTML form?

It is simply this: the HtmlForm class working on the server generates any needed HTML code for a form but also runs some code to set view state information for the controls in the form. That's the importance of playing by the rules of ASP.NET.

Is there a way to use multiple server-side forms in ASP.NET applications? As mentioned earlier, the built-in code of ASP.NET pages raises an exception if more than one server-side form is rendered—regardless of whether you created it programmatically or declaratively. A possible workaround entails the creation of server components that are logically equivalent to forms. Such components are all contained within a single server-side form, but provide you with the subset of functionality that allows them to gather and submit some input data to an external page. The simplest way to create such embeddable components is through user controls.

Using User Controls

The idea is that you create a user control for each logical form you need to display. The final structure of the page looks like this:

<form runat="server">
   <msdn:MyRegForm runat="server" id="userRegForm"
    action="register.aspx" />
   <hr>
   <msdn:MyLogForm runat="server" id="userLogForm"
    action="login.aspx" />
</form>

In addition to managing child controls, the user controls also provide a basic set of features, such as the Action property. The following code snippet shows a sample form user control:

<%@ Control ClassName="RegisterForm" Inherits="MsdnMag.MyUserForm" %>
<b>Register</b><br>
<asp:textbox runat="server" id="RegUserName" /><br>
<asp:textbox runat="server" id="RegPassword" textmode="password" />
<br>
<asp:button runat="server" text="Register" onclick="Submit" />

You don't need to add an Action property and a Submit method to each user control. You can simply derive user controls from a base class. In my sample code, I've called the base class MsdnMag.MyUserForm (see Figure 9).

Figure 9 MyUserForm Base Class (Close Figure 9)
namespace MsdnMag
{
    public class MyUserForm : System.Web.UI.UserControl
    {
        public string Action;

        protected virtual void Submit(object sender, System.EventArgs e)
        {
            foreach(object o in Page.Request.Form)
            {
                // NOTE that : is an implementation detail that could
                // change in future implementations
                string ctlName = o.ToString();
                if (!ctlName.StartsWith(UniqueID + ":"))
                    continue;
                string[] parts = ctlName.Split(':');

                // Use the last part of the control name as the
                // key for the Context collection. The post value
                // is correctly retrieved using the full name
                Context.Items[parts[parts.Length-1]] =
                    Page.Request.Form[ctlName];
            }

            Page.Server.Transfer(Action);
        }
    }
}

The base class provides a public property called Action and a Submit method that can be used as an event handler. When the user clicks on a submit button on a similar user control, the code extracts all the request form values that relate to the user control. A user control is a naming container, which means that the name of its child controls is prefixed by the ID of the user control. The foreach loop in Figure 9 walks its way through the page's Request object and selects all the values that belong to the user controls. Note that I am making assumptions on the : symbol, which actually is an implementation detail that might change in the future. These values are cached in the HttpContext object. HttpContext represents the context of the request being processed. It is a global object whose life spans the life of the request. After filling the Context.Items collection, the Submit method transfers the control to the page specified by the Action property.

How can this page retrieve the values of the caller form? Of course, it gets input data reading from the context of the request:

<html>
<body>
  User <b>
    <%= Context.Items["RegUserName"] %></b> has been registered.
      Password is <b>
    <%= Context.Items["RegPassword"] %></b>.
</body>
</html>

In terms of programming style, you use Context.Items in much the same way you used Request.Form in ASP pages. Context is more efficient than Session because it doesn't persist any longer than the page instance. Context.Items is an effective, generic way of passing parameters to pages run using Server.Transfer or Server.Execute.

Looking at user controls from this perspective, one drawback quickly emerges. You must create and register a new .ascx file for each logical form you need to employ, like this:

<%@ Register TagPrefix="msdn" TagName="MyRegForm"
 src="register.ascx" %>
<%@ Register TagPrefix="msdn" TagName="MyLogForm"
 src="login.ascx" %>

On the other hand, you can't define the contents of a user control inline in the host page as if it were a <form> or a <div> tag. What about extending the ASP.NET control that represents a <div> tag to derive a new container control that exposes an Action property and a Submit method?

Using a Custom Form Control

The <div> tag has the built-in capability of parsing any child tags. By deriving a new class, you can acquire the same capability. The <div> tag is represented on the server through a generic class called HtmlGenericControl. This class is generic because it doesn't provide a programming interface tailor-made to the features of the <div> control. ASP.NET utilizes the HtmlGenericControl to render a handful of HTML tags when these are used with the runat attribute. Generic HTML controls include the <div>, <font>, <span>, and <body> elements. Figure 10 shows the source code of the MsdnMag.Form class that inherits from HtmlGenericControl. By default, the constructor of the HtmlGenericControl class generates a <span> tag. If you want it to create another HTML container, use the proper constructor overload:

public class Form : HtmlGenericControl, INamingContainer
{
   public Form(string tag) : base("div")
   {...}

Figure 10 MsdnMag.Form (Close Figure 10)
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace MsdnMag
{
    public class Form : HtmlGenericControl, INamingContainer
    {
        public Form(string tag) : base("div")
        {
        }

        public string Action;

        public virtual void Submit(object sender, EventArgs e)
        {
            foreach(object o in Page.Request.Form)
            {
                string ctlName = o.ToString();
                if (!ctlName.StartsWith(UniqueID + ":"))
                    continue;
                string[] parts = ctlName.Split(':');
                Context.Items[parts[1]] = Page.Request.Form[ctlName];
            }

            Page.Server.Transfer(Action);
        }
    }
}

When the user clicks on the submit button of the logical form, a predefined Submit method executes. It looks up all the input controls in the logical form and copies their values in the HTTP context. The INamingContainer interface is necessary to guarantee that all the controls belonging to the logical form, and no others, are loaded into the context. INamingContainer is a marker interface that means that no methods are to be implemented. However, a class that exposes this interface enjoys special treatment from the ASP.NET framework. In particular, the child controls are given an ID that is prefixed with the ID of the parent. As for user controls—the base UserControl class implements INamingContainer itself—you can check the prefix of the child controls referenced in the overall page's Request.Form collection and select only those contained in your logical form. Note that this is not the only way you can obtain the same information.

In order to register the new Form class with the page, you use a variant of the @Register directive, as shown here:

<%@ Register tagprefix="msdn" Namespace="MsdnMag" Assembly="MyForm" %>
Since Form is a custom control, you must first compile it into an assembly and make it available within the ..\bin folder of the application's virtual directory and the application's virtual directory or the Global Assembly Cache (GAC). Figure 11 and Figure 12 show the source code and the output of a page that uses the techniques that have been examined so far.

Figure 11 New Source Code (Close Figure 11)
<%@ Page ClassName="MultiFormPage" %>
<%@ Register TagPrefix="msdn" TagName="MyRegForm" src="register.ascx" %>
<%@ Register TagPrefix="msdn" TagName="MyLogForm" src="login.ascx" %>
<%@ Register tagprefix="msdn" Namespace="MsdnMag" Assembly="MyForm" %>

<script language="C#" runat="server">
</script>

<html>
<head><title>Pro ASP.NET (Ch04)</title></head>
<body>
    <form runat="server">
        <msdn:MyRegForm runat="server" id="userRegForm"
         action="register.aspx" />
        <hr>
        <msdn:MyLogForm runat="server" id="userLogForm"
         action="login.aspx" />
        <hr>

        <msdn:form id="myRegForm" runat="server" action="register.aspx">
            <b>Register</b><br>
            <asp:textbox runat="server" id="RegUserName" /><br>
            <asp:textbox runat="server" id="RegPassword"
             textmode="password" /><br>
            <asp:button runat="server" text="Register"
             onclick="myRegForm.Submit" />
        </msdn:form>

        <msdn:form id="myLogForm" runat="server" action="login.aspx">
            <b>Login</b><br>
            <asp:textbox runat="server" id="LogUserName" /><br>
            <asp:textbox runat="server" id="LogPassword"
             textmode="password" /><br>
            <asp:button runat="server" text="Log in"
             onclick="myLogForm.Submit" />
        </msdn:form>
    </form>
</body>
</html>
Figure 12 Output
Figure 12 Output

Popup Forms

To top off the discussion about ASP.NET forms, let's analyze what happens with popup forms. The HTML 4.0 object model allows you to use the window.showModalDialog method to show a modal window with a Web page inside. ASP.NET pages can be used with popup windows, but with some caveats. If the page has read-only contents, then no special care need be taken. You just open the window using boilerplate client-side script code and let it go. By contrast, if the page shown through a modal window is interactive and posts back, some measures should be taken to ensure that all the generated output goes through the modal dialog until the user closes it.

As an alternative to client-side modal dialogs, to show logically modal forms you could resort to the trick I discussed earlier—building multiple forms using only ASP.NET code (see Figure 7). Let's drill down a bit.

To perform a modal operation—a blocking operation that prevents interaction with the main page until terminated—a Web page can resort to the following JavaScript code:

<script language="JavaScript">
function ShowPopup() {
  window.showModalDialog("dialog.aspx", "Dialog",
     "dialogWidth:200px;dialogHeight:300px;");
}
</script>
<html>
<body>
    <input type="button" onclick="ShowPopup()" value="Popup" />
</body>
</html>
The showModalDialog method on the window object opens the specified page in a Windows® dialog box with the given title and styles. (For more information about the styling options of the dialog see the MSDN® documentation.) The previous code pops up a 200×300 pixel modal window filled with the HTML content generated by dialog.aspx. So far, so good.

Suppose now that the dialog.aspx page contains some links and buttons you can click. Again, think of a modal page you want to pop up for user registration and logging. The user enters some data and clicks; the page posts back, but the output is displayed in a new browser window. Any value you assign to the form's target value is useless. The behavior is by design, but is there a workaround? Yes, for the browsers that support it, use a wrapper inline frame. Instead of containing the code to show, dialog.aspx contains an <iframe> tag. Part of the HTML 4.0 specification, the <iframe> element allows authors to insert a frame within a block of text. The source document that goes in the frame is designated by the Src attribute:

<iframe src="Pages/multiforms.aspx" name="embeddedFrame"
width="100%" height="100%" />

Figure 13 shows an ASP.NET popup application in action.

Figure 13 ASP Popup
Figure 13 ASP Popup

A Quick Look at Mobile Controls

ASP.NET mobile controls, an extension of ASP.NET desktop server controls, are aimed at building wireless applications. ASP.NET mobile controls are server controls that require the runat attribute to be set to server. Since they execute on the server, they can exploit the potential of the .NET Framework in spite of the modest amount of memory typically available on wireless devices. All mobile controls derive from the MobileControl class which, in turn, inherits from Control. Because of this relationship, both server and mobile controls share a common set of properties, methods, and events. Mobile Web Forms, on the other hand, descend from the MobilePage class. ASP.NET mobile controls include a couple of controls—Panel and Form—that act as containers of other controls. These controls are different in that the Form supports the posting of data, whereas the Panel represents a simple static group of related controls. Multiple panels can be nested and included in forms, but forms themselves cannot be nested.

The Form control represents the outermost container of controls in a mobile page. A page can contain multiple forms that you can address individually from within the application. This sets Mobile Web Forms apart from ASP.NET Web Forms in which only one server-side form is permitted. Mobile ASP.NET never combines multiple forms on a single screen, but uses them in a mutually exclusive way.

The Form control features common-use properties such as Action and Method. Action defaults to the empty string, which causes a postback to the same URL. A form fires events when activated and deactivated.

When multiple forms are active on the same page, you can move from one to another using a Link control. To make a Link control point to an internal form, you set the NavigateURL property to the ID of the form prefixed with a # symbol, like this:

<mobile:Form id="Form2" runat="server">
   <mobile:Label runat="server"
        text="Second page of information" />
   <mobile:Link runat="server" NavigateURL="#Form1" text="Back" />
</mobile:Form>

The ActiveForm property on the mobile page class gets and sets the currently active form. When a page is initially rendered, the first form in the page is automatically activated and displayed. On subsequent postbacks, another form might be brought to the foreground, either programmatically or as the result of user navigation. Implementing a similar model in ASP.NET is not particularly difficult, but probably requires that you inherit your pages from an application-specific base class whose interface is extended with properties and methods to handle the currently active form.

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 most of his time teaching classes on ASP.NET and speaking at conferences. Dino is currently writing Programming ASP.NET for Microsoft Press. Get in touch at dinoe@wintellect.com.


The XML Files: Web Services Encoding and More

XML Files
Web Services Encoding and More
Aaron Skonnard

Code download available at: XmlFiles0305.exe (339KB)

Q What's the difference between document/literal and rpc/encoded Web Services? What's the history behind them?

A There are two SOAP message styles, called document and rpc. Document style indicates that the SOAP body simply contains an XML document. The sender and receiver must agree on the format of the document ahead of time, such as traditional messaging systems like Microsoft® Message Queue (MSMQ), MQSeries, and so on. The agreement between the sender and receiver is typically negotiated through literal XML Schema definitions. Hence, this combination is referred to as document/literal.

RPC (Remote Procedure Call) style, on the other hand, indicates that the SOAP body contains an XML representation of a method call such as the traditional distributed component technologies of DCOM, Corba, and others. RPC style uses the names of the method and its parameters to generate structures that represent a method's call stack (see section 7 of the SOAP 1.1 specification at http://www.w3c.org/TR/SOAP). These structures can then be serialized into the SOAP message according to a set of encoding rules. The SOAP specification defines a standard set of encoding rules for this purpose (see section 5 of the SOAP 1.1 spec) that codify how to map the most common programmatic data structures, such as structs and arrays, into an XML 1.0 format. Since RPC is traditionally used in conjunction with the SOAP encoding rules, the combination is referred to as rpc/encoded.

The document/literal approach is more straightforward and easier for toolkits to get right because it simply relies on XML Schema to describe exactly what the message looks like on the wire. SOAP, however, was created before XML Schema existed. And back then they were focused primarily on objects (hence the O in SOAP), which led to the rpc/encoded way of doing things. Since universal description languages such as XML Schema or Web Services Description Language (WSDL) weren't available back then, the rpc/encoded style had to assume that additional metadata would be available for describing the method call (such as a type library, Microsoft .NET Framework assembly, or Java class file).

Such metadata made it possible for toolkits to automatically map between existing components and SOAP messages without explicitly defining what the messages looked like ahead of time.

Looking back, rpc/encoded is mostly seen as a stop-gap measure that seemed reasonable in a world without XML Schema. Now that XML Schema is here to stay, document/literal is quickly becoming the de facto standard among developers and toolkits thanks to its simplicity and interoperability results.

The WS-I Basic Profile actually disallows the use of SOAP encoding. This is a good indication that rpc/encoded will indeed become deprecated over time. For more information on this, see "The Argument Against SOAP Encoding" on MSDN® Online.

ASP.NET WebMethods support both document/literal and rpc/encoded formats. Figure 1 provides two sample WebMethods; one uses document/literal and the other rpc/encoded. As you can see, document/literal is the default style for WebMethods. You must annotate the WebMethod with SoapRpcMethod (or the class with SoapRpcService) to use rpc/encoded instead.

Figure 1 Document/Literal vs. rpc/Encoded (Close Figure 1)
[WebMethod]
public Person MarryDocLit(Person p1, Person p2)
{
    p1.spouse = p2;
    // next line will cause an exception during serialization
    // p2.spouse = p1;
    return p1;
}

[WebMethod]
[SoapRpcMethod]
public Person MarryRpcEnc(Person p1, Person p2)
{
    p1.spouse = p2;
    p2.spouse = p1;
    return p1;
}

The SOAP style is described in the generated WSDL definition so consumers know what to expect. Figure 2 shows what the WSDL binding looks like for each operation that is shown in Figure 1. Notice that the style is specified on the soap:operation element while the encoding technique is specified on the soap:body element's use attribute.

Figure 2 WSDL Binding Definition (Close Figure 2)
  <binding name="Service1Soap" type="s0:Service1Soap">

    <operation name="MarryDocLit">
      <soap:operation style="document"
         soapAction="http://example.org/MarryDocLit" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>

    <operation name="MarryRpcEnc">
      <soap:operation style="rpc"
         soapAction="http://example.org/MarryRpcEnc" />
      <input>
        <soap:body use="encoded"
           namespace="http://example.org/"
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
      </input>
      <output>
        <soap:body use="encoded"
           namespace="http://example.org/"
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
      </output>
    </operation>

  </binding>

Even though these WebMethods have the identical C# signatures, the SOAP messages look quite different on the wire due to the differences in style and use. Figure 3 illustrates the response from the document/literal WebMethod while Figure 4 shows the response from the rpc/encoded version.

Figure 4 RPC/Encoded Response Message (Close Figure 4)
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:tns="http://example.org/"
    xmlns:types="http://example.org/encodedTypes"
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/
      encoding/">
    <tns:MarryRpcEncResponse>
      <MarryRpcEncResult href="#id1" />
    </tns:MarryRpcEncResponse>
    <types:Person id="id1" xsi:type="types:Person">
      <name xsi:type="xsd:string">Bob Smith</name>
      <spouse href="#id2" />
    </types:Person>
    <types:Person id="id2" xsi:type="types:Person">
      <name xsi:type="xsd:string">Mary Smith</name>
      <spouse href="#id1" />
    </types:Person>
  </soap:Body>
</soap:Envelope>
Figure 3 Document/Literal Response Message (Close Figure 3)
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <MarryDocLitResponse xmlns="http://example.org/">
      <MarryDocLitResult>
        <name>Bob Smith</name>
        <spouse>
          <name>Mary Smith</name>
        </spouse>
      </MarryDocLitResult>
    </MarryDocLitResponse>
  </soap:Body>
</soap:Envelope>

This example also highlights one area where rpc/encoded is actually more capable than document/literal today. The SOAP encoding rules facilitate the representation of object graphs in XML such as the cyclical reference shown in Figure 4. Since there is no way to represent this with just XML Schema, the document/literal example will throw an exception if you try to return something that cannot be represented as a natural tree structure. This issue will probably be solved by defining a standard way to represent object graphs in conjunction with XML Schema. Despite this deficiency, you're still better off avoiding rpc/encoded if you really care about interoperability across today's Web Service toolkits.

Q In ASP.NET, how can I attach an image to a SOAP message using Direct Internet Message Encapsulation (DIME)?

A Microsoft ships an implementation of DIME in Web Services Enhancements (WSE) 1.0 for Microsoft .NET. In order to begin using the WSE in your WebMethods, you need to register a SoapExtension (called WebServicesExtension) in your web.config file, as illustrated here:

<configuration>
  <system.web>
    <webServices>
      <soapExtensionTypes>
        <add type="Microsoft.Web.Services.
           WebServicesExtension,
           Microsoft.Web.Services,Version=1.0.0.0,
           Culture=neutral,
           PublicKeyToken=31bf3856ad364e35"
           priority="1" group="0"/>
      </soapExtensionTypes>
    </webServices>
  </system.web>
</configuration>

Assuming you have the assembly referenced in your project, you should be able to take advantage of the Microsoft.Web.Services namespaces in your code. Attaching an image to the SOAP response is as simple as creating a DimeAttachment object and adding it to the HttpSoapContext.ResponseContext.Attachments collection. I've provided a sample DIME Web Service (called DimeServer) that exposes a single operation named GetImage. GetImage takes the name of the image file you want to retrieve from its image repository (found in the vroot's child image directory). See Figure 5 for the WebMethod code.

Figure 5 DIME Server (WebMethod) Code (Close Figure 5)
using Microsoft.Web.Services;
using Microsoft.Web.Services.Dime;

[WebService(Namespace="http://example.org/dime/")]
public class DimeServer : WebService
{
  [WebMethod]
  public void GetImage(string name)
  {
    DimeAttachment dimeAttach = new DimeAttachment(
      "image/jpg", TypeFormatEnum.MediaType,
      Server.MapPath(String.Format("images/{0}", name)));
    HttpSoapContext.ResponseContext.Attachments.Add(dimeAttach);
  }
}

The response generated by GetImage is no longer straight SOAP; it's now a DIME message with two records. The first record is the SOAP response; the second message is the binary image. Hence, a DIME processor is needed on the client as well. Luckily, the WSE bits can be used on the client side in a similar fashion. All you have to do is change the name of the base class in your generated proxy class from SoapHttpClientProtocol to WebServicesClientProtocol. Then you can extract the binary image from the proxy's ResponseSoapContext property, as illustrated in Figure 6.

Figure 6 DIME Client Code (Close Figure 6)
private void btnRetrieve_Click(...)
{
  try
  {
    DimeServer ds = new DimeServer();
    ds.GetImage(txtFileName.Text);

    if (ds.ResponseSoapContext.Attachments.Count > 0)
      pbResults.Image = new Bitmap(
        ds.ResponseSoapContext.Attachments[0].Stream);
  }
  catch(Exception ex)
  {
    MessageBox.Show(ex.Message);
  }
}
Figure 7 Dime Client
Figure 7 Dime Client

Figure 7 shows the client application in action. You can download the complete sample from the link at the top of this article. For more information make sure you check out "Programming with Web Services Enhancements 1.0 for Microsoft .NET".

Q Is it possible to send and receive raw XML using a WebMethod?

A Yes, it's possible to deal with raw XML documents—at least in the form of Document Object Model (DOM) trees—by defining your WebMethod as follows:

[WebMethod]
public XmlNode GenericOperation(XmlNode input)
{
  // process XML input and return another DOM tree
  •••
}

In this case, GenericOperation is capable of taking any XML document as input and returning an XML document as output. Using this approach basically tells the WebMethod framework that you're not interested in mapping the input/output XML documents to .NET Framework classes like you normally would. Hence, you're left processing the input document and generating the output document using traditional DOM techniques, which may or may not be what you want.

Although this approach gives you a lot of flexibility, it's important to realize what it means to clients. If you inspect the generated WSDL document for this endpoint, you'll notice that both the Request and Response elements contain wildcards (see Figure 8). This means that the client won't know anything about what you're trying to send by looking at the WSDL definition; you'll have to coordinate this information out-of-band or with a custom WSDL document. Interestingly, the same issue exists with ADO.NET DataSets. For more information on using DataSets with WebMethods, see the April 2003 installment of the The XML Files. And since this technique typically forces clients to work with the XML directly, most developers prefer to use strongly typed signatures.

Figure 8 GenericOperation XML Schema Definition (Close Figure 8)
  <s:element name="GenericOperation">
   <s:complexType>
     <s:sequence>
       <s:element minOccurs="0" name="input">
         <s:complexType mixed="true">
           <s:sequence>
             <s:any />       <!-- wildcard -->
           </s:sequence>
         </s:complexType>
       </s:element>
     </s:sequence>
   </s:complexType>
 </s:element>
 <s:element name="GenericOperationResponse">
   <s:complexType>
     <s:sequence>
       <s:element minOccurs="0"
         name="GenericOperationResult">
         <s:complexType mixed="true">
           <s:sequence>
             <s:any />       <!-- wildcard -->
           </s:sequence>
         </s:complexType>
       </s:element>
     </s:sequence>
   </s:complexType>
 </s:element>

I know developers who prefer to use strongly typed signatures, but in some cases would also like access to the raw XML from within the WebMethod. The trick that makes this possible is to rewind the request message stream by setting its Position back to 0 (it's advanced during the WebMethod deserialization process). Once you've done that, you can process the stream using an XmlTextReader or by loading it into an XmlDocument, as shown in the following code snippet:

[WebMethod]
public string Add(double x, double y)
{
  Stream in = HttpContext.Current.Request.InputStream;
  in.Position = 0;
  XmlDocument doc = new XmlDocument();
  doc.Load(in);

  // process raw XML input via doc
  •••
}

At least this gives you a hybrid technique for reading the input. Dealing with the output is a little trickier because the framework doesn't expect you to write directly to the output stream. See the March 2003 House of Web Services column for more on this technique.

Q How do you load a string of XML into the contents of a specific node using the System.Xml DOM of the Microsoft .NET Framework?

A One way to do this is to load the string of XML into a new XmlDocument object and then import the nodes into the original XmlDocument object. This is somewhat tedious, however, and it assumes that the string of XML is a complete document and not a fragment. The XmlNode class provides an easier way to solve this problem through its InnerXml property.

XmlNode provides InnerXml and OuterXml properties that are similar to the DHTML innerHTML and outerHTML properties. Retrieving the OuterXml property returns the XML 1.0 representation of the entire node, including the XML for the node you're on. Getting the InnerXml property, on the other hand, only returns the XML 1.0 representation of the current node's contents.

Although the OuterXml property is read-only, the InnerXml property also allows you to write XML 1.0 into a given node, overwriting its content. Doing so causes a new tree of DOM nodes to be generated and inserted on the fly. Here's an example that illustrates both properties in action:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<person><overwrite_me/></person>");
XmlNode person = doc.DocumentElement;
person.InnerXml = "<name>Marti</name><age>30</age>";
person.InnerXml += "<gender>female</gender>";
Console.WriteLine(person.OuterXml);
Running this program produces the following console output:
<person>
<name>Marti</name><age>30</age><gender>female</gender>
</person>
Notice that setting InnerXml (using =) overwrites the overwrite_me element. Using the += operator, on the other hand, adds the new XML to the node's children collection without overwriting what was already there.

These properties are not part of the W3C DOM specification, but they are handy additions that can simplify moving between XML 1.0 and parts of an in-memory DOM tree.

Q In your column on the Birth of Web Services, you provide a definition of Web Services. Does the W3C agree on a definition that drives the work they're doing?

A As I said in the column, the overall success of Web Services depends first and foremost on everyone agreeing on its meaning. Around the beginning of 2002, the W3C formed the Web Services Architecture Working Group, chartered to define a reference architecture for Web Services (see http://www.w3.org/2002/ws/arch). They provide the following definition in their current Working Draft:

A Web service is a software system identified by a URI, whose public interfaces and bindings are defined and described using XML. Its definition can be discovered by other software systems. These systems may then interact with the Web service in a manner prescribed by its definition, using XML based messages conveyed by Internet protocols http://www.w3.org/TR/2002/WD-ws-arch-20021114/#whatisws.

Interestingly, by their definition, an application doesn't require the use of SOAP or WSDL to be considered a Web Service. If you read the note that immediately follows the definition, however, you'll learn that their reference architecture does assume (in a somewhat schizophrenic way) that the higher levels of the Web Services protocol stack are indeed built on the foundation of SOAP and WSDL. In the end, the Working Group realizes that a common foundation is needed for building additional layers like security, routing, choreography, and so forth (see the GXA specs for more examples) but at the same time, they don't want to discourage experimentation and research. The most controversial area of Web Services research is that of Representational State Transfer (REST), which offers some interesting ideas on how Web Services could more fully embrace the architecture of today's Web. I suppose the Working Group's definition leaves the door open for such ideas.

I also pointed out in the October 2002 column that the Web Services Interoperability Organization (WS-I) is focused on the same problem. One of the WS-I's main goals is to "articulate and promote a common industry vision for Web services..." (see http://ws-i.org/FAQ.aspx#A02), which will ultimately help interoperability. The WS-I complements the W3C because it's more focused on practical issues like providing implementation guidelines.

The WS-I is working to produce a Web Services basic profile (see Basic Profile Version 1.0), which in my opinion serves as a more concrete and practical definition today—it essentially states that a Web Service must support XML 1.0, XML Schema, SOAP, WSDL, and Universal Description, Discovery, and Integration (UDDI) to comply with it.

Q When I try to validate an XML document for which the processor cannot find the XML Schema definition, the processor still says it's valid. Why?

A XML Schema defines two validation techniques that can be used by processors: lax and strict. Lax validation means that the processor should validate if schema information is available, while strict validation means that the processor must validate or produce an error if schema information is not available. For example, when using wildcards (xsd:any), XML Schema allows you to specify which form of validation you'd like the processor to use:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:tns="urn:person" targetNamespace="urn:person">
  <xsd:complextType name="OpenEndedPerson">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:any processContents="lax"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:element name="person" type="tns:OpenEndedPerson"/>
</xsd:schema>

The following XML document contains a person element, which may or may not be valid, according to the previous schema:

<x:person xmlns:x="urn:person">
  <name>Bob</name>
  <z:skillSet xmlns:z="urn:skills">
    <program>WordPerfect</program>
    <program>Microsoft Office</program>
    <program>Visio</program>
  </z:skillSet>
</x:person>

When the processor validates this instance, it will first look for the appropriate schema information (like the complexType that describes the skillSet element). If it's able to locate the schema information, it will continue performing validation on skillSet's structure; otherwise it will skip the element and assume it's valid (note: in addition to strict and lax, you can also specify "skip," which tells the processor not to validate the given element).

Wildcards, however, only apply to local elements. But what about the root element? Unfortunately, XML Schema doesn't provide a way to specify or constrain what's allowed as the root element of a document. In other words, the root element of a document can be any global element in the schema. If there were such a declaration, it could allow you to specify whether to use lax or strict (like wildcards), and if you specified strict, you wouldn't run into what you're experiencing. XML Schema chose a different approach.

If the processor cannot locate schema information for the root element, it assumes that the element is of type ur-type (or anyType). Because anyType sits at the top of the XML Schema type hierarchy, everything is a valid instance of anyType. I agree that it can be misleading when the processor tells you an instance is valid when, in fact, it didn't really analyze the structure of the element. This will always be the case if the schema's targetNamespace doesn't match the namespace of the root element in the instance document (I see this bug all the time).

It's possible to catch this issue if you're willing to write a little code. If you're using MSXML 4.0, it's actually quite easy—all you have to do is explicitly call the validate function after loading the document. The validate function produces an error if it can't locate a type definition for the root element, unlike the call to load (even with validateOnParse="true"). See Figure 9 for a simple example.

Figure 9 Validation with MSXML 4.0 (Close Figure 9)
var dom = new ActiveXObject("MSXML2.DOMDocument.4.0");
var sc = new ActiveXObject("MSXML2.XMLSchemaCache.4.0");

// load schema into cache
sc.add("http://example.org/person", "person.xsd");
dom.schemas = sc;
dom.async = false;

if (dom.load("person.xml")) // did it really validate?
{
  // calling validate makes sure it found a definition
  // for the root element
  var err = dom.validate();
  if (err.errorCode == 0)
    WScript.echo("success: document conforms to DTD/Schema");
  else
    WScript.echo("### error: " + err.reason);
}
else
  WScript.echo("### error: " + dom.parseError.reason);

If you're writing code using the .NET Framework with System.Xml, you have to do a little more. You'll need to wrap an XmlValidatingReader around the instance document, load the schema into the XmlValidatingReader's schema cache (see the Schemas property), and then explicitly move the cursor to the root element (by calling MoveToContent). Once positioned on the root element, the SchemaType property provides access to its XML Schema type definition if found. Hence, if SchemaType is null, you know it didn't locate the correct schema and, therefore, didn't perform validation. Figure 10 provides a complete example.

Figure 10 Validation with System.Xml (Close Figure 10)
try
{
  // prepare validating reader for instance document
  XmlTextReader tr = new XmlTextReader("person.xml");
  XmlValidatingReader vr = new XmlValidatingReader(tr);

  // load schema into vr's schema cache
  XmlTextReader schemaReader = new XmlTextReader("person.xsd");
  XmlSchema schema = XmlSchema.Read(schemaReader, null);
  vr.Schemas.Add(schema);

  // move vr to root element (skips whitespace/comments)
  // and check for type definition explicitly
  vr.MoveToContent();
  if (vr.SchemaType == null)
    throw new Exception("Cannot locate schema definition");

  // load document with validating reader
  XmlDocument doc = new XmlDocument();
  doc.Load(vr);
  Console.WriteLine("success: document conforms to DTD/Schema");

}
catch(XmlException e)
{
    // catches XML 1.0 errors
    Console.WriteLine("### error: " + e.Message);
}
catch(XmlSchemaException e)
{
    // catches schema validation errors
    Console.WriteLine("### error: " + e.Message);
}
catch(Exception e)
{
    // everything else
    Console.WriteLine("### error: " + e.Message);
}

Q Should I use DateTime.Parse, Convert.ToDateTime, or XmlConvert.ToDateTime when parsing dates from an XML file?

A It depends on the lexical format of the dates you're trying to parse. Convert.ToDateTime simply delegates to DateTime.Parse, which knows how to read date strings in different culture-specific formats that you can control:

// these are equivalent
DateTime dt1 = Convert.ToDateTime("2/16/1992 12:15:12");
DateTime dt2 = DateTime.Parse("2/16/1992 12:15:12");

All of Convert's ToXxx methods that accept a string will delegate to the corresponding common language runtime (CLR) type's Parse method. In general, Parse knows how to read CLR-defined lexical representations for the given type (for example, Convert.ToDouble delegates to Double.Parse).

XmlConvert, on the other hand, was defined specifically for reading the lexical representations of XML Schema's data types:

// reads XSD lexical representation for dates
XmlConvert.ToDateTime("1992-02-16T12:15:12")
If you're working with an XML Schema-based application, this is the one you typically want to use. For example, writing code to process XML messages automatically generated by the .NET Framework WebMethod infrastructure requires you to process XML Schema representations, which means using XmlConvert.

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.


Advanced Basics: Windows Forms Controls

Advanced Basics
Windows Forms Controls
Ken Spencer

Code download available at: AdvancedBasics0305.exe (144KB)
Contents
Conclusion

Q I have two questions about building Windows® Forms controls. First, how do you build a control that allows the user to drag and drop other controls onto the new control at run time? Second, how do you allow the user to move the control around on a form?

A The answers to these questions was not obvious from the documentation, but they turned out to be fairly simple once I figured out the basics. I created a sample application to demonstrate the solution. The application retrieves a list of the tables from a SQL Server™ database and inserts them in a TreeView control. Then you can drag a table name and drop it onto the form to add the new user control. Once you have created the new user control, you can also add the columns in a table by dragging them and dropping them onto the new control, as shown in Figure 1.

Figure 1 Dropping Controls on a Form
Figure 1 Dropping Controls on a Form

In this column, I'll cover only some of the control's code, but the download at the link at the top of this article contains the complete application. I derived my control from the UserControl class, which provides a generic custom control named Lister. Next, I added a label control to the top of the user control. The label will contain the title for the control. Then I added a ContextMenu control to the user control and named it mnuFitHeight. The items in the menu are shown in Figure 2.

Figure 2 Menu
Figure 2 Menu

Now, let's take a look at how to handle the visual operations. I set up this control to allow the user to grab the label and move the control. The MouseDown event is used to trap the user's mouse click and store the coordinate data:

Private Sub lblTitle_MouseDown(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles lblTitle.MouseDown
        localMouseIsDown = True
        localXClickedPosition = e.X
        localYClickedPosition = e.Y
End Sub
The localMouseIsDown variable is used to indicate that the control is being moved. The other two variables hold the X and Y locations, which correspond to the left and top.

The MouseMove event procedure fires the DragStarted event. This allows any application that is hosting this control to pick up the move operation by using this event:

Private Sub lblTitle_MouseMove(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles lblTitle.MouseMove
    If localMouseIsDown And localAllowDragMove Then
    RaiseEvent DragStarted(Me, Me.Location.X, _
        Me.Location.Y)
    End If
End Sub

The real work of the move operation is performed by the label's MouseUp event. When the user releases the mouse, this event actually moves the control. The move only occurs if the AllowDragMove public property is set to True. You can set this property to False, which prevents moving the control. The localAllowDragMove variable mirrors the AllowDragMove property locally in the procedure.

The localXClickedPosition and localYClickedPosition variables are used to track the X and Y coordinates of the mouse when the user clicked it. This allows the move to place the control relative to the user's click point and release point. If either of these variables are greater than 0, they are subtracted from the final location of the control. Finally, the new coordinates are set and used to create a new point which is used to place the control in its final position (see Figure 3).

Figure 3 Locating the Control (Close Figure 3)
Private Sub lblTitle_MouseUp(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles lblTitle.MouseUp
    Dim x, y As Integer

    If localAllowDragMove Then
        If localXClickedPosition > 0 Then
            x = Me.Location.X + e.X - localXClickedPosition
        Else
            x = Me.Location.X + e.X
        End If
        If localYClickedPosition > 0 Then
            y = Me.Location.Y + e.Y - localYClickedPosition
        Else
            y = Me.Location.Y + e.Y
        End If
        Me.Location = New Point(x, y)
        localMouseIsDown = False
    End If
End Sub

The Add method of the control simply adds a new checkbox to the user control and then wires up the event handlers for the new checkbox using the AddressOf method to point to the event handler code.

Now let's switch to Form1, which uses the Lister control. When the form loads, the tree is loaded with tables and the corresponding column names for those tables. How do you allow the user to drag a table or column name and drop it onto the form? First, you must create the ItemDrag event for the TreeView. This is where you set up the drag and drop operation. The following code creates a new variable (trNode) of type TreeNode and sets it to the instance of the node selected by the user. Then the DoDragDrop function is called to set up the drag effects (copy in this instance):

Private Sub trvwColumns_ItemDrag(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.ItemDragEventArgs) _
    Handles trvwColumns.ItemDrag
    Dim trNode As TreeNode
    trNode = CType(e.Item, TreeNode)
    DoDragDrop(trNode, DragDropEffects.Copy)
End Sub

If you don't set up this event, drag and drop won't work. The form's DragEnter event handles the start of the drag and drop operation. If the data is present (GetDataPresent call), then the code checks to make sure the tree node is a root node because you do not want to add a new instance of a Lister control for a column name, only a table name. You can see that handling the DragEnter event properly provides you fine control over what is dropped onto the form or control (see Figure 4).

Figure 4 Handling the DragEnter Event (Close Figure 4)
Private Sub frm_DragEnter(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.DragEventArgs) _
    Handles MyBase.DragEnter
    Dim FullPath As String
    Dim i As Integer
    Dim NewControlName As String
    Dim NewNode As TreeNode
    Try
        If e.Data.GetDataPresent(_
        "System.Windows.Forms.TreeNode") _
        Then
        NewNode = CType(e.Data.GetData( _
            "System.Windows.Forms.TreeNode"), TreeNode)
        FullPath = NewNode.FullPath
        i = InStr(FullPath, "\")
        'Determine if item is table (root node)
        If i = 0 Then
            NewControlName = "lst" & NewNode.Text
            If Not CheckFormForThisListItem( _
                NewControlName) _
                Then
                    e.Effect = DragDropEffects.Copy
            End If
        End If
        End If
    Catch exc As Exception
    MsgBox("Error " & exc.Message)
    End Try
End Sub

Now, let's turn to the form's DragDrop event handler, shown in Figure 5. This event performs a number of tasks. This is where drag and drop operations become really interesting because a number of things usually happen during the drop phase. In this example, the code makes sure it is the right node type and then calls the FindControlOnForm method to determine if a control by that name already exists on the form. If it does, then the drop does not take place and the user is presented with a message box indicating that only tables can be dropped.

Figure 5 DragDrop Event Handler (Close Figure 5)
Private Sub frm_DragDrop(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)_
    Handles MyBase.DragDrop
    Dim x, y As Integer
    Dim FullPath, NewControlName As String
    Dim oControl As Control
    Dim oListControl, oNewListControl As Lister
    Dim i As Integer
    Dim NewLabel As New Label()
    Dim LocalMousePosition As Point
    Dim oNewCheckLister As Lister
    Dim NewNode As TreeNode

    x = e.X
    y = e.Y
    Try
If e.Data.GetDataPresent(_
    "System.Windows.Forms.TreeNode") Then
    NewNode = CType(e.Data.GetData(_
        "System.Windows.Forms.TreeNode"), TreeNode)
    FullPath = NewNode.FullPath
    NewControlName = "lst" & NewNode.Text
    If IsNothing(FindControlOnForm( _
        NewControlName)) Then
        i = InStr(FullPath, "\")
        If i > 0 Then
            MsgBox( Only tables may be dropped here")
        Else
            oNewListControl = New Lister()
            oNewListControl.Name = NewControlName
            oNewListControl.AllowDragMove = True
            oNewListControl.Title = FullPath
            oNewListControl.AllowDrop = True
            LocalMousePosition = Me.PointToClient( _
                New Point(x, y))
            oNewListControl.Location = _
                LocalMousePosition
                Me.Controls.Add(oNewListControl)
            'Add control to form
            Me.Controls.Add(oNewListControl)
            AddHandler oNewListControl.DragEnter, _
                AddressOf TableListBoxDragEnter
            AddHandler oNewListControl.DragDrop, _
                AddressOf TableListBoxDragDrop
            AddHandler oNewListControl.ItemChecked, _
                AddressOf ListItemChecked
        End If
    End If
End If
    Catch exc As Exception
        MsgBox("Error " & exc.Message)
    End Try
End Sub

If the dropped node is a root node (table name), then a new instance of the Lister control is created and its properties are set. Next, the new control is added to the Controls collection of the form. Note that you can add controls to tab pages and other container controls using this technique. After the control is added to the Controls collection, the AddHandler statement is used to wire up the event handlers for the new control. This allows you to dynamically add event handlers for things like DragDrop and other events you're interested in.

Now let's check out the events for the Lister control. The TableListBoxDragEnter procedure handles the DragEnter event for the new control. When a user performs a drag and drop operation and enters the new control, this event makes sure the dragged item is a child node, then sets up the drag and drop operation. Notice that the signature of this event matches the DragEnter event even though the Handles clause is not used (see Figure 6).

Figure 6 Make Sure Item is Child Node (Close Figure 6)
Sub TableListBoxDragEnter(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)
    Dim FullPath As String
    Dim i As Integer
    Dim NewNode As TreeNode
    Dim TableName As String
    Dim ListCurrent As Lister

    ListCurrent = CType(sender, Lister)

    Try
        If e.Data.GetDataPresent("System.Windows.Forms.TreeNode") Then
            NewNode = _
            CType(e.Data.GetData("System.Windows.Forms.TreeNode"), _
                TreeNode)

            FullPath = NewNode.FullPath
            i = InStr(FullPath, "\")
            If i > 0 Then
                TableName = FullPath.Substring(0, i - 1)
                If LCase(TableName) = LCase(ListCurrent.Title) Then
                    e.Effect = DragDropEffects.Copy
                End If
            End If
        End If
    Catch exc As Exception
        MsgBox("Error " & exc.Message)
    End Try
End Sub

The other half of the drag and drop operation is the DragDrop event shown here. This code grabs a reference to the dropped node and calls the AddListBoxItem method to add this node to the Lister control:

Sub TableListBoxDragDrop(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.DragEventArgs)
    Dim ListCurrent As Lister
    Dim NewNode As TreeNode
    ListCurrent = CType(sender, Lister)
    NewNode = CType(e.Data.GetData(_
        "System.Windows.Forms.TreeNode"), TreeNode)

    AddListBoxItem(ListCurrent, NewNode.Text)
End Sub

The AddListBoxItem procedure which is called by the DragDrop code simply calls the Add method of the Lister control to add the new item to the user control:

Sub AddListBoxItem(ByVal ListCurrent As Lister, _
    ByVal Item As String)
    ListCurrent.Add(Item, Item, False)
    ListCurrent = Nothing
End Sub

Now, let me show you one other little issue I uncovered in building and using this control. I added the AllowDragMove property to allow you to turn this capability on or off. My first attempt at this property looked like this:

<Browsable(True)> _
Property AllowDragMove() As Boolean
    Get
        Return localAllowDragMove
    End Get
    Set(ByVal Value As Boolean)
        AllowDragMove = Value
    End Set
End Property
When I tried the control on a form, setting the property in the Properties window didn't work. I could select True or False, but the actual value did not change.

After puzzling over this for a while, I finally realized my mistake. In the Set part of the property, I was setting the AllowDragMove property instead of setting the localAllowDragMove variable. Here's the correct code:

<Browsable(True)> _
Property AllowDragMove() As Boolean
    Get
        Return localAllowDragMove
    End Get
    Set(ByVal Value As Boolean)
        localAllowDragMove = Value
    End Set
End Property

Whenever you are having problems with controls or properties, always remember to check that the proper variables are being set.

Conclusion

As you can see, working with drag and drop operations is pretty straightforward once you understand how the DragEnter and DragDrop events work. You also need to understand how specific events for particular controls work, such as the TreeView control's ItemDrag event.

The Microsoft® .NET Framework has a rich set of classes for building applications, which make visual operations like drag and drop or move simple to implement.

Send your questions and comments for Ken to basics@microsoft.com.

Ken Spencerworks for 32X Tech (http://www.32X.com), where he provides training, software development, and consulting services on Microsoft technologies.


House of Web Services: Mandatory Headers in ASP.NET Web Services

House of Web Services
Mandatory Headers in ASP.NET Web Services
Tim Ewald

Code download available at: WebServices0305.exe (138KB)
Contents
Mandatory Headers
Mandatory Headers in ASP.NET
Expecting the Unexpected
Solving the Problem Once and for All
What About WSE?
Where are We?

The ASP.NET Web Services infrastructure includes support for programming with SOAP message headers. Unfortunately, the model for handling mandatory headers is flawed in that you need to write additional code so that a Web Service will not execute when a mandatory header is not processed. This column explores a specific problem that arises when you deal with mandatory SOAP handles and presents three solutions.

Mandatory Headers

SOAP messages may contain an optional header element from the SOAP namespace that, as stated in version 1.1 of the SOAP specification, "provides a flexible mechanism for extending a message in a decentralized and modular way without prior knowledge between the communicating parties." If present, the header element contains zero or more extension elements. An extension element may be marked with an optional mustUnderstand attribute, also from the SOAP namespace, indicating that the extension is mandatory. Figure 1 shows a sample message with a mandatory header. There is broad consensus that if a message receiver cannot process a mandatory header, it shouldn't process the message that contains it. Section 4.2.3 of the SOAP 1.1 specification suggests this, though the wording is a little vague:

Figure 1 Mandatory Header Sample (Close Figure 1)
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header>
    <!-- the mustUnderstand attribute's value indicates
         that this header is mandatory -->
    <ns:Foo xmlns:ns="http://msdn.microsoft.com/hows"
            env:mustUnderstand="1">
      <ns:Baz>Some info...</ns:Baz>
    </ns:Foo>
  </env:Header>
  <env:Body>
    •••
  </env:Body>
</env:Envelope>
If a header element is tagged with a SOAP mustUnderstand attribute with a value of '1', the recipient of that header entry either [must] obey the semantics (as conveyed by the fully qualified name of the element) and process correctly to those semantics, or [must] fail processing the message.

The Candidate Recommendation of the SOAP 1.2 specification (Part 1, section 2.4) fixes this problem, making the semantics of mandatory header processing very clear:

Mandatory SOAP header blocks are presumed to somehow modify the semantics of other SOAP header blocks or SOAP body elements. Therefore, for every mandatory SOAP header block targeted to a node, that node [must] either process the header block or refrain from processing the SOAP message at all, and instead generate a fault (see 2.6 Processing SOAP Messages and 5.4 SOAP Fault).

The ASP.NET Web Services plumbing includes a model for dealing with SOAP message headers. Unfortunately, it will allow a message to be processed even when it contains a mandatory header that the target method is not aware of or does not understand. While a fault will be generated, that doesn't happen until after your Web Service's code has run.

Mandatory Headers in ASP.NET

When the ASP.NET Web Services message dispatcher pulls a message apart, it uses the Body element's contents as the input parameters to a method invocation. Before the call is invoked, the contents of the header element (if any) may be deserialized into fields or properties of the target object. When the method runs, it can examine those fields or properties to see what headers a request message contained. The mapping of headers to properties or fields is optional. It is controlled using the value of System.Web.Services.Protocols.SoapHeaderAttribute. An example of this attribute is shown in Figure 2.

Figure 2 Mapping Headers to Properties or Fields (Close Figure 2)
// This class represents a SOAP header
[XmlRoot("Foo", Namespace="http://msdn.microsoft.com/hows")]
public class Foo : SoapHeader
{
  public string Baz;
}

public class SampleService : WebService
{
  // If a SOAP message's Header element includes a {http://
  // msdn.microsoft.com/hows}Foo element, it will be deserialized into
  // this field before the target message runs
  Foo foo;

  // The [SoapHeader] attribute tells the message dispatcher to use the
  // field foo to deserialize the appropriate header if it appears; the
  // dispatcher uses reflection to determine which header (if any) to map
  // to foo
  [WebMethod]
  [SoapHeader("foo")]
  public string Bar()
  {
    StringBuilder sb = new StringBuilder("Bar method ran, ");

    // The Bar method can access the foo field when it runs; if the
    // request message did not include
    // a {http://msdn.microsoft.com/hows}Foo header, foo will be null
    if (this.foo != null)
      sb.Append("foo is null");
    else
      sb.Append(string.Format("foo.Baz is {0}", foo.Baz));
    return sb.ToString();
  }
}

The Bar method is marked with a [SoapHeader] attribute that refers to the field named foo. The message dispatcher uses reflection and discovers that foo is an instance of Foo, a subclass of System.Web.Services.Protocols.SoapHeader. The dispatcher knows that all SoapHeader-derived types represent XML elements that may appear inside a request message's Header element. In this case, the [XmlRoot] attribute tells the dispatcher that the Foo class represents the Foo header from the http://msdn.microsoft.com/hows namespace. If that header appears in the header of a request message, the dispatcher then deserializes it into foo, thus making it available when Bar runs.

The SoapHeader base class (not to be confused with the SoapHeaderAttribute class) has a Boolean property called MustUnderstand. When the message dispatcher deserializes a header to a field or property, it checks to see if the header is mandatory (that is, it checks to see if the XML element has a mustUnderstand attribute whose value is either 1 or true). If it is, the dispatcher sets the SoapHeader.MustUnderstand property of the corresponding field or property to true. A method can examine this field to determine whether a header is mandatory or optional. A modified version of the Bar method that indicates whether a given Foo header was mandatory is shown in Figure 3.

Figure 3 MustUnderstand Property (Close Figure 3)
[WebMethod]
[SoapHeader("foo")]
public string Bar()
{
  StringBuilder sb = new StringBuilder("Bar method ran, ");

  if (this.foo != null)
    sb.Append("foo is null");
  else
  {
    // Note that the generated string now includes
    // information about whether header was mandatory or optional
    sb.Append(string.Format("foo.Baz is {0} and {1}",
                            foo.Baz, foo.MustUnderstand ?
                            "mandatory" : "optional"));
  }

  return sb.ToString();
}

The Web Services message dispatcher uses the SoapHeader.MustUnderstand property to tell your code that a given header is mandatory. But the SOAP specification says (vaguely or not, depending on the version) that if a mandatory header is not understood, a fault must be returned. That means that your code has to tell the dispatcher which headers it understood. To that end, the SoapHeader class has a second property called DidUnderstand. After a method call completes, the plumbing checks to see that all mandatory headers were understood by examining the DidUnderstand property of every mandatory SoapHeader-derived field or property used during the course of that method. If any of them have a MustUnderstand value of true and a DidUnderstand value of false, a fault is generated indicating that a mandatory header was not understood.

Figure 4 shows a third implementation of the Bar method that sets the DidUnderstand property of the foo field whenever the header is mandatory.

Figure 4 DidUnderstand Property (Close Figure 4)
[WebMethod]
[SoapHeader("foo")]
public string Bar()
{
  StringBuilder sb = new StringBuilder("Bar method ran, ");

  if (this.foo != null)
    sb.Append("foo is null");
  else
  {
    sb.Append(string.Format("foo.Baz is {0} and {1}",
                            foo.Baz, foo.MustUnderstand ?
                            "mandatory" : "optional"));

    // tell dispatcher that mandatory header
    // was understood so plumbing doesn't generate a fault
    if (foo.MustUnderstand)
      foo.DidUnderstand = true;
  }

  return sb.ToString();
}

Expecting the Unexpected

At first glance, the MustUnderstand/DidUnderstand model may seem quite reasonable, but it has a significant limitation. The fundamental problem is that sometimes a client may send you mandatory headers that you were not expecting. For instance, my sample service may expect that the Foo header is optional, but it is coded to work correctly even if the client makes the header mandatory. This is easy to do with any header that your service is expecting. If you map a header to a field or property, just make sure that you set its DidUnderstand value to true whenever necessary. (Or, for safety's sake, set it to true in all cases—it won't hurt anything when a header is optional.)

But what about headers your service is not expecting? What if your client decides to slip in a mandatory header that your service has never heard of? If your service doesn't know about a header, it can't have mapped it to a specific field or property, and without that mapping there is no way to know if an unknown header is mandatory or to indicate that it was understood. At the same time, the plumbing has to enforce the SOAP specification's requirement that mandatory headers be processed correctly. So what happens?

Optimally, if the dispatcher detects a mandatory header that it can't map to a field or property, it will generate a fault instead of executing the code. Of course, this solution assumes that all mandatory headers are mapped to members within your class. And that isn't a bad assumption, given the general header processing model. Unfortunately, that's not the way it works. Instead, it's your job to map unknown headers to a field or property too and see if there are any mandatory headers you don't understand. The System.Web.Services.Protocols.SoapUnknownHeader class exists for this very purpose.

SoapUnknownHeader is a subclass of SoapHeader that the Web Services message dispatcher uses to represent any header that is not mapped to a specific field or property. Figure 5 shows a new version of my sample service that handles unknown headers.

Figure 5 SOAPUnknownHeader (Close Figure 5)
public class SampleService : WebService
{
  Foo foo;

  // array to hold unknown headers
  SoapUnknownHeader unknownHeaders[];

  // second SoapHeader attribute tells the message dispatcher to map all
  // headers except Foo to unknown headers array
  [WebMethod]
  [SoapHeader("foo")]
  [SoapHeader("unknownHeaders")]
  public string Bar()
  {
    // check all expected headers and mark as understood
    if (foo != null && foo.MustUnderstand)
      foo.DidUnderstand = true;

    // check all unexpected headers and fail if any are mandatory
    foreach(SoapUnknownHeader header in unknownHeaders)
    {
      // if a header is mandatory...
      if (header.MustUnderstand)
      {
        // build string describing problem
        string err = string.Format(
          "Mandatory header: {{{0}}}{1}",
          header.Element.NamespaceURI,
          header.Element.LocalName);

        // throw mustUnderstand fault, as required by SOAP specification
        throw new SoapException(err,
          new XmlQualifiedName("mustUnderstand",
          "http://schemas.xmlsoap.org/soap/envelope/"));
      }
    }

    // do actual work after header processing is done

    StringBuilder sb = new StringBuilder("Bar method ran, ");

    if (this.foo != null)
      sb.Append("foo is null");
    else
      sb.Append(string.Format("foo.Baz is {0} and {1}",
                              foo.Baz, foo.MustUnderstand ?
                              "mandatory" : "optional"));

    return sb.ToString();
  }
}

As with other SoapHeader-derived types, you use the [SoapHeader] attribute to tell the message dispatcher what to do with any headers it can't otherwise process. The new version of the Bar method includes an attribute that tells the plumbing to map any unknown headers to the unknownHeaders field (which needs to be an array, in case there is more than one unknown header). The method begins with header processing code. First, it marks any mandatory Foo header (mapped to the foo field) as understood. Second, if the client sent any unknown headers (mapped to the unknownHeaders field), the method checks to see if any of them are mandatory. If it finds a mandatory header that it doesn't understand, it generates a MustUnderstand SOAP fault, as defined by the SOAP specification.

I'm willing to bet that the vast majority of ASP.NET Web Services that have been written and deployed to date do not explicitly check for unknown mandatory headers. That means that if a client sends a message with a mandatory header that has not been mapped to a field or property, there will be no way for a method to mark the header as understood. The result is that the dispatcher will generate a fault, but not until after the service executes! That's a direct violation of the SOAP specification, which says that a message must not be processed if mandatory headers are not understood. Practically, it means that your service will run, think that everything succeeded, and the client will receive a SOAP fault.

In short, if you want your service to conform to the SOAP specification, you need to do some work to handle unknown mandatory headers correctly.

Solving the Problem Once and for All

The SoapUnknownHeader code in Figure 5 solves the unknown mandatory header problem. However, including that code in every method you expose as a Web Service would be tedious. Luckily, a much simpler solution is possible: put it in a SoapExtension. The complete source code for a MustUnderstandExtension that detects the presence of unknown mandatory headers before a service executes is shown in Figure 6.

Figure 6 MustUnderstandExtension (Close Figure 6)
// MustUnderstandExtension checks for unknown mandatory
// headers and generates a fault if any are found
public class MustUnderstandExtension : SoapExtension
{
  public override object GetInitializer(Type serviceType)
  { return null; }

  public override object GetInitializer(
                      LogicalMethodInfo methodInfo,
                      SoapExtensionAttribute attribute)
  { return null; }

  public override void Initialize(object initializer) {}

  // ProcessMessage looks for message headers and
  // and handles unknown mandatory headers correctly
  public override void ProcessMessage(SoapMessage message)
  {
    switch(message.Stage)
    {
      // process headers after they've been deserialized
      // into data members
      case SoapMessageStage.AfterDeserialize:
      {
        foreach(SoapHeader header in message.Headers)
        {
          if (header.GetType() != typeof(SoapUnknownHeader))
          {
            // if header is *known* (e.g., Foo), mark it as understood
            header.DidUnderstand = true;
          }
          else if (header.MustUnderstand)
          {
            // if header is of *unknown* AND mandatory, generate a fault
            SoapUnknownHeader unknownHeader = (SoapUnknownHeader) header;
            string err = string.Format(
              "Mandatory header: {{{0}}}{1}",
              unknownHeader.Element.NamespaceURI,
              unknownHeader.Element.LocalName);
            throw new SoapException(err,
              new XmlQualifiedName("mustUnderstand",
              "http://schemas.xmlsoap.org/soap/envelope/"
              ));
          }
        }
      }
      break;
    }
  }
}

The ProcessMessage method scans all the headers found in a request message. It marks any known header as understood (that is, any header that has been mapped to a field or property of some subclass of SoapHeader other than SoapUnknownHeader). If it encounters an unknown mandatory header, it generates a SOAP MustUnderstand fault. This works even if a Web Service class does not include a field or property of type array of SoapUnknownHeader. This solution assumes that any known headers are in fact understood and that all mandatory headers are known, but, again, that's a reasonable assumption.

Given the general applicability of this extension, it makes sense to configure it for an entire Web Service, not just a single method. You can do that by adding the following configuration info to your web.config (or even machine.config) file:

<configuration>
  <system.web>
    <webServices>
      <soapExtensionTypes>
        <add type="MustUnderstand.MustUnderstandExtension,
                   MustUnderstand"
         priority="1" group="0" />
      </soapExtensionTypes>
    </webServices>
  </system.web>
</configuration>
Once that's done, the SampleService I just presented can revert to the one shown in Figure 7. Note that the code for processing unknown headers has been removed. With the MustUnderstandExtension in place, it is no longer necessary.

Figure 7 SimpleService with MustUnderstandExtension (Close Figure 7)
[WebMethod]
[SoapHeader("foo")]
public string Bar()
{
  StringBuilder sb = new StringBuilder("Bar method ran, ");

  if (this.foo != null)
    sb.Append("foo is null");
  else
  {
    // Note that the generated string now includes information about
    // whether header was mandatory or optional
    sb.Append(string.Format("foo.Baz is {0} and {1}",
                            foo.Baz, foo.MustUnderstand ?
                            "mandatory" : "optional"));
  }

  return sb.ToString();
}

What About WSE?

A growing number of ASP.NET Web Services use the Web Services Enhancements (WSE) for the Microsoft® .NET Framework class library to add support for advanced Web Service protocols to their applications. The WSE has a very different model for processing headers, so if you're using it, you need to solve the mandatory unknown header problem a different way.

When a message arrives in a process, the WSE passes it through a series of input filters. Each input filter consumes one or more headers, removes them from the SOAP message, and exposes the data they carry as part of the SoapContext object model the WSE associates with the message. While it is possible to mix this header processing model with the ASP.NET Web Services header processing model, it's simpler to use the WSE model alone.

The easiest way to solve the mandatory unknown header problem with the WSE is to implement a custom input filter like the one shown in Figure 8. The MustUnderstandInputFilter uses XPath to see if a message contains a mandatory header. If it does, the filter generates a fault. The assumption is that by the time this input filter runs, previous input filters have processed and removed all mandatory headers from the message. (As a side note, it is important that anyone writing custom input filters for the WSE should adopt this approach; otherwise solving the mandatory unknown header problem will be more difficult than necessary.) If you configure this input filter using the standard WSE technique, it will run after all the WSE's built-in input filters. The configuration entry you need is shown in the following code:

<configuration>
  <microsoft.web.services>
    <filters>
      <input>
        <add type="MustUnderstandInputFilter, MustUnderstand" />
      </input>
    </filters>
  </microsoft.web.services>
</configuration>

Figure 8 Custom Input Filter (Close Figure 8)
public class MustUnderstandInputFilter : SoapInputFilter
{
  public override void ProcessMessage(SoapEnvelope msg)
  {
    // create and initialize namespace manager for XPath engine
    XmlNamespaceManager namespaces = new XmlNamespaceManager(msg.NameTable);
    namespaces.AddNamespace("soap",
        "http://schemas.xmlsoap.org/soap/envelope/");

    // look in message for any mandatory header element
    XmlNode node = msg.SelectSingleNode(
      "/soap:Envelope/soap:Header/*[@soap:mustUnderstand
       [. = '1' or . = 'true']]", namespaces);

    // if any mandatory headers are found, generate a fault
    if (node != null)
    {
      string err = string.Format("Mandatory header: {{{0}}}{1}",
        node.NamespaceURI, node.LocalName);
      throw new SoapException(err, new XmlQualifiedName("mustUnderstand",
        "http://schemas.xmlsoap.org/soap/envelope/"));
    }
  }
}

Where are We?

Much of the power of the SOAP protocol comes from its extensibility. The header element is critical to the further development of Web Services; all the new advanced protocols for security, transactions, and other features are implemented using headers. However, this flexibility comes at a price. In loosely coupled systems, you need to expect that a client will send you things you didn't expect and you need to deal with them appropriately. In the case of mandatory headers, that means detecting and processing them before your Web Service code executes. While the ASP.NET Web Services plumbing does not do this automatically, its header processing model allows you to solve the problem yourself, and the solution is pretty simple.

Send your questions and comments for Tim to housews@microsoft.com.

Tim Ewaldis a Program Manager for XML Web Services at Microsoft. He is currently redesigning the MSDN architecture around Web Services, and writing and speaking about that technology. He is the author of Transactional COM+: Designing Scalable Applications (Addison-Wesley, 2001). Reach Tim at tewald@microsoft.com.


Basic Instincts: Programming Events of the Framework Class Libraries

Basic Instincts
Programming Events of the Framework Class Libraries
Ted Pattison

Contents
The EventHandler Delegate
Customized Event Parameters
Parameterizing Custom Events
Wrap-up

This month's installment represents the final column in a series of three focusing on programming events. In the previous two columns, I showed you how to define and raise events (see Basic Instincts: Programming with Events Using .NET and Basic Instincts: Static Event Binding Using WithEvents). I also explained how to wire up event handlers using both dynamic and static event binding. This month I am going to conclude my coverage of events by showing some practical examples of handling some of the more commonly used events in the Microsoft® .NET Framework.

The EventHandler Delegate

When you build applications using Windows® Forms or ASP.NET, you'll observe that a significant percentage of the events you encounter are defined in terms of a generic delegate type named EventHandler. The EventHandler type exists in the System namespace and has the following definition:

Delegate Sub EventHandler(sender As Object, e As EventArgs)

The delegate type EventHandler defines two parameters in its calling signature. The first parameter, named sender, is based on the generic Object type. The sender parameter is used to pass a reference that points to the event source object. For example, a Button object acting as an event source will pass a reference to itself when it raises an event based on the EventHandler delegate type.

The second parameter defined by EventHandler is named e and is an object of type EventArgs. In many cases, an event source passes a parameter value equal to EventArgs.Empty, indicating there is no additional parameter information. If an event source wants to pass extra parameterized information in the e parameter, it should pass an object created from a class that derives from the EventArgs class.

Figure 1 shows an example involving two event handlers in a Windows Forms application that are wired up using static event binding. Both the Load event of the Form class and the Click event of the Button class are defined in terms of the delegate type EventHandler.

Figure 1 Using Static Event Binding (Close Figure 1)
Imports System
Imports System.Windows.Forms

Public Class MyApp : Inherits Form

    '*** static event handler for base class event
  Private Sub Form1_Load(ByVal sender As Object, _
                         ByVal e As EventArgs)_
                         Handles MyBase.Load
    '*** event handler code
  End Sub

    '*** button defined as WithEvents field
  Friend WithEvents cmdDoTask As Button

    '*** static event handler for button
  Private Sub cmdDoTask_Click(ByVal sender As Object, _
                              ByVal e As System.EventArgs) _
                              Handles cmdDoTask.Click
    '*** event handler code
  End Sub

End Class

You should also note that the names and format of the two event handler methods in Figure 1 are consistent with what is generated for you by the Visual Studio® .NET IDE. For example, if you double-click on a form or command button while you are in design view, Visual Studio .NET will automatically create the skeleton of event handler methods that looks like this. All you are required to do is fill in the implementation for these methods to give your event handlers the desired behavior.

You might have noticed that the Visual Studio .NET IDE generates handler methods using the naming scheme that was required by Visual Basic® 6.0. However, you should remember that the names of handler methods don't really matter with static event binding in Visual Basic .NET. It's the Handles clause that matters. You should feel free to rename handler methods to anything you want.

It's possible to rewrite these two event handlers so that they are wired up using dynamic event binding instead of static event binding. For example, the Form-derived class in Figure 2 provides the exact same event binding behavior as the Form-derived class in Figure 1. The only difference is that the latter example uses dynamic event binding and does not require either the WithEvents keyword or the Handles keyword. In many cases, you will write implementations for handler methods based on the EventHandler delegate type without referencing either the sender parameter or the e parameters. For example, these parameter values are of no real use when you are writing a handler for the Load event of a Form-derived class. The sender doesn't provide any value because it simply passes your Me reference. The e parameter passes EventArgs.Empty:

Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        '*** these tests are always true
        Dim test1 As Boolean = sender Is Me
        Dim test2 As Boolean = e Is EventArgs.Empty
End Sub

Figure 2 Using Dynamic Event Binding (Close Figure 2)
Imports System
Imports System.Windows.Forms

Public Class MyApp : Inherits Form
  Friend cmdDoTask As Button

  Sub New()
    '*** other initialization code omitted
    AddHandler MyBase.Load, AddressOf Me.Handler1
    AddHandler cmdDoTask.Click, AddressOf Me.Handler2
  End Sub

  Private Sub Handler1(ByVal sender As Object, ByVal e As EventArgs)
    '*** event handler code
  End Sub

  Private Sub Handler2(ByVal sender As Object, _
                       ByVal e As System.EventArgs)
    '*** event handler code
  End Sub
End Class

You might wonder why the calling signature of the Load event isn't more customized for its needs. After all, it would be less confusing if the Load event didn't include any parameters at all. It's fairly easy to find other examples of events based on the EventHandler delegate type in which the sender parameter or the e parameter don't pass anything of value.

Ask yourself the following questions. Why do you think so many events have been modeled in terms of the EventHandler when this delegate type has such a generic calling signature? Why didn't the designers of the .NET Framework model each event in terms of a custom delegate with a calling signature that was fine-tuned for its needs? As it turns out, there was a design goal in the development of the .NET Framework to restrict the number of delegates used for event handling. A little more explanation is in order.

The first motivation for minimizing the number of delegate types has to do with a more efficient utilization of memory used by an application. Loading in more types means using up more memory. If every event defined by the classes within the Windows Forms framework were based on a custom delegate, hundreds of delegate types would have to be loaded into memory every time you ran a Windows Forms application. The Windows Forms framework can provide much better memory utilization by relying on a small handful of delegate types for the hundreds of events defined within the Form class and the various control classes.

A second motivation for minimizing the number of delegate types has to do with increasing the potential to achieve polymorphism with pluggable handler methods. When you write a handler method with a calling signature that matches the EventHandler delegate, you can bind it to the majority of the events raised by a form and its controls.

Let's look at a few examples of writing generic event handlers. I'll begin with an example in which you want to respond to the TextChanged event of several textboxes on a form by changing the user's input to uppercase. There's no need to create a separate event handler for each control. Instead, you can create a single event handler and bind it to the TextChanged event of several different textboxes (see Figure 3).

Figure 3 One Event Handler for Several Controls (Close Figure 3)
Public Class MyApp : Inherits Form
  Friend WithEvents TextBox1 As TextBox
  Friend WithEvents TextBox2 As TextBox
  Friend WithEvents TextBox3 As TextBox
  '*** create event handler bound to several TextChanged events
  Private Sub TextChangedHandler(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs) _
                                 Handles TextBox1.TextChanged, _
                                         TextBox2.TextChanged, _
                                         TextBox3.TextChanged
    '*** convert sender to TextBox
    Dim txt As TextBox = CType(sender, TextBox)
    txt.Text = txt.Text.ToUpper()
  End Sub
End Class

The first thing you should note about this example is that a Handles clause isn't limited to a single event. You can include as many events as you'd like by using a comma-delimited list after the Handles keyword. In this example, the TextChangedHandler method is used to create three different event handlers. Therefore, this method is going to execute whenever the user changes the text in any of the three textboxes.

When the TextChangedHandler method executes, how do you know which TextBox object is raising the event? That's what the sender parameter is for. Remember that the sender parameter is passed in terms of the generic type Object. That means you must convert it to a more specific type before you can program against it. In the previous example, the sender parameter must be converted to a TextBox in order to access its Text property.

If you have experience building form-based applications with earlier versions of Visual Basic, you might be accustomed to using control arrays. A primary advantage to using control arrays in Visual Basic 6.0 is that this feature makes it possible to create a single handler method that responds to events raised by several different controls. Visual Basic .NET does not support control arrays. However, you should not be overly alarmed because, as you've just seen, Visual Basic .NET provides an alternate technique for binding a single handler method to several different events.

The event architecture of the .NET Framework also provides you with the ability to do things that have never been possible with control arrays. For example, you can create a single handler method to respond to events raised by several different types of controls. An example of a handler method that's bound to three different events on three different control types is shown in Figure 4.

Figure 4 Handler Method Bound to Different Events (Close Figure 4)
Public Class MyApp : Inherits Form
  Friend WithEvents TextBox1 As TextBox
  Friend WithEvents CheckBox1 As CheckBox
  Friend WithEvents ListBox1 As ListBox
  '*** define form-wide dirty flag
  Friend DirtyFlag As Boolean
  '*** set dirty flag when various events are raised
  Private Sub DirtyFlagHandler(ByVal sender As Object, _
                               ByVal e As EventArgs) _
              Handles TextBox1.TextChanged, _
                      CheckBox1.CheckedChanged, _
                      ListBox1.SelectedIndexChanged
    '*** set form-wide dirty flag
    DirtyFlag = True
  End Sub
End Class

As you can see, the scheme for binding handler methods to events is pretty flexible. The only requirement is that a handler method and the events it's bound to are based on the same delegate type. The fact that so many events in the .NET Framework are based on the EventHandler delegate type makes it easy to write generic handler methods.

When you write a generic handler method, it's sometimes necessary to write code to perform conditional operations that are only executed when the event source is a certain type of object. For example, your handler method can inspect the sender parameter using the TypeOf operator. This allows your handler method to execute one set of operations if the event source is a Button object and another set of operations if it's a CheckBox object, like this:

Sub GenericHandler1(sender As Object, e As EventArgs)
  If (TypeOf sender Is Button) Then
    Dim btn As Button = CType(sender, Button)
    '*** program against btn
  ElseIf (TypeOf sender Is CheckBox) Then
    Dim chk As CheckBox = CType(sender, CheckBox)
    '*** program against chk
  End If
End Sub

Customized Event Parameters

An event notification based on the EventHandler delegate doesn't typically send any meaningful information in the e parameter. The e parameter is often useless because it contains either a value of EventArgs.Empty or a value of Nothing. However, the designers of the .NET Framework created a convention for passing parameterized information from an event source to its event handlers. The convention involves the creation of a custom event argument class and a custom delegate type.

The mouse events raised by the Form class provide a good example of how this convention should be used. Parameterized information about the mouse position and about which mouse button has been pressed are modeled in a class named MouseEventArgs. The MouseEventArgs class contains an X and a Y property to track the mouse position as well as a Button property to indicate which mouse button has been pressed. Note that by convention the MouseEventArgs class must inherit from the generic class EventArgs.

The convention for passing parameterized information in an event notification requires a custom delegate to complement the custom event argument class. Therefore, there is a delegate named MouseEventHandler to complement the class MouseEventArgs. The handler delegate has this definition:

Delegate Sub MouseEventHandler(sender As Object, e As MouseEventArgs)

Now let's say you'd like to respond to a mouse-related event such as the MouseDown event of the Form class. You can write a handler method that looks like the one shown in Figure 5.

Figure 5 MouseEventHandler (Close Figure 5)
Private Sub Form1_MouseDown(ByVal sender As Object, _
                            ByVal e As MouseEventArgs) _
                            Handles MyBase.MouseDown
  '*** capture mouse position
  Dim x_position As Integer = e.X
  Dim y_position As Integer = e.Y

  '*** take action depending on which button was pressed
  Select Case e.Button
    Case MouseButtons.Left
      '*** do something
    Case MouseButtons.Right
      '*** do something else
  End Select

End Sub

Note that the e parameter is very useful in the implementation of this handler method. The e parameter is used to determine the mouse position as well as to determine which mouse button has been pressed. All this parameterized information has been made possible by the design of the MouseEventArgs class.

You can find other examples of this parameterization convention used in the Windows Forms framework. For example, there is a class named KeyPressEventArgs that is complemented by a delegated type named KeyPressEventHandler. In addition, the ItemChangedArgs class is complemented by a delegate type named ItemChangedHandler. You will likely encounter other events with parameterized information that follow this same convention.

Parameterizing Custom Events

As an exercise, let's design a custom event to follow this convention for parameterization. I am going to use an example similar to what I used in my last few columns involving a BankAccount class. Consider the following code snippet:

Class BankAccount
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      '*** raise event
    End If
    '*** perform withdrawal
  End Sub
End Class

Assume you are required to raise an event whenever a BankAccount object experiences a withdrawal for an amount greater than $5,000. When you raise this event, you are required to pass all the registered event handlers the amount of the withdrawal as a parameter. First, you should create a new event argument class that inherits from the EventArgs class:

Public Class LargeWithdrawArgs : Inherits EventArgs
    Public Amount As Decimal
    Sub New(ByVal Amount As Decimal)
        Me.Amount = Amount
    End Sub
End Class

A custom event argument class should be designed to contain a public field for each parameterized value an event source needs to pass to its event handler. In this case, the LargeWithdrawArgs class has been designed with a Decimal field named Amount. Next, you must create a new delegate type to complement your new event argument class:

Delegate Sub LargeWithdrawHandler(ByVal sender As Object, _
                                  ByVal e As LargeWithdrawArgs)
By convention, this delegate type has been defined with an Object parameter named sender as the first parameter. The second parameter, e, is based on the custom event argument class.

Now that you have created the custom event argument class and a complementary delegate type, you can put them to use. Examine the following class definition:

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      Dim args As New LargeWithdrawArgs(Amount)
      RaiseEvent LargeWithdraw(Me, args)
    End If
    '*** perform withdrawal
  End Sub
End Class

The LargeWithdraw event has been modified to use the standard convention in the .NET Framework for passing parameterized information in an event notification. When it's time to raise a LargeWithdraw event in the Withdraw method, it's necessary to create a new instance of the LargeWithdrawArgs class and pass it as a parameter. Since the BankAccount object is the one that is raising the event, the Me keyword can be used to pass the sender parameter, as shown here:

Dim args As New LargeWithdrawArgs(Amount)
RaiseEvent LargeWithdraw(Me, args)

Now that you have seen how to create the event source, let's turn our attention to creating a handler method for this event. A handler method will be able to retrieve the parameterized information it needs through the e parameter. In this case, a handler method will use the e parameter to retrieve the value of the Amount field:

Sub Handler1(sender As Object, e As LargeWithdrawArgs)
  '*** retrieve parameterized information
   Dim Amount As Decimal = e.Amount
End Sub

Figure 6 shows a complete app in which a BankAccount object sends out event notifications when a large withdrawal is made. Note that this app follows the standard common language runtime convention for passing parameterized information in an event.

Figure 6 Custom Parameterized Events (Close Figure 6)
'*** custom event arguments class
Class LargeWithdrawArgs : Inherits EventArgs
  Public Amount As Decimal
  Sub New(ByVal Amount As Decimal)
    Me.Amount = Amount
  End Sub
End Class

'*** delegate to complement custom event arguments class
Delegate Sub LargeWithdrawHandler(ByVal sender As Object, _
                                  ByVal e As LargeWithdrawArgs)

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      Dim args As New LargeWithdrawArgs(Amount)
      RaiseEvent LargeWithdraw(Me, args)
    End If
    '*** perform withdrawal
  End Sub
End Class

Class AccountAuditor
  Private WithEvents account As BankAccount
  Sub Handler1(ByVal sender As Object, _
               ByVal e As LargeWithdrawArgs) _
               Handles account.LargeWithdraw
    '*** retrieve parameterized information
    Dim Amount As Decimal = e.Amount
  End Sub
  Sub New(ByVal SourceAccount As BankAccount)
    Me.account = SourceAccount ''*** triggers binding of event handler
  End Sub
End Class

Module MyApp
  Sub Main()
    '*** create bank account object
    Dim account1 As New BankAccount()
    '*** register event handlers
    Dim listener1 As New AccountAuditor(account1)
    '*** do something that triggers callback
    account1.Withdraw(5001)
  End Sub
End Module

Wrap-up

This concludes my series on the fundamentals of programming with events using Visual Basic .NET. The first two columns showed the mechanics of raising and handling events. This month I concentrated on practical examples of programming with common events and delgates defined within the .NET Framework.

It is likely that the majority of events you will handle with Visual Basic .NET will be based on the EventHandler delegate. You have seen that it's possible to wire up several events to a single handler method. In cases like this, it's important that you know when and how to utilize the sender parameter. You have also seen other events that pass parameterized information using a custom argument class. All in all, you should now be prepared to work with an event-driven framework such as Windows Forms or ASP.NET.

Send your questions and comments for Ted to instinct@microsoft.com.

Ted Pattisonis an instructor and course writer at DevelopMentor (http://www.develop.com). He has written several books about Visual Basic and COM and is currently writing a book titled Building Applications and Components with Visual Basic .NET to be published by Addison-Wesley in 2003.


C++ Q&amp;A: Window Destruction in the .NET Framework

C++ Q&A
Window Destruction in the .NET Framework
Paul DiLascia

Code download available at: CQA0305.exe (120KB)

Q I have a question about window objects (Forms and Controls) based on your November 2002 column about nondeterministic destruction in the Microsoft® .NET Framework. With MFC, I can put cleanup code in my destructor because MFC deletes the window object automatically when the window is destroyed. What about with the .NET Framework? If a window is destroyed, does the .NET Framework immediately destroy the corresponding Form, or does it wait until a garbage collection to do this? If the .NET Framework doesn't free the Form object right away, how can I release my resources as soon as the window is destroyed? How does this relate to the Dispose pattern described in your column?

Zachary Morton

A Good questions! Other readers have asked me about window destruction and it's such an important topic, I decided to devote a whole column to it. But first let me point out that it's not quite accurate to say that MFC destroys window objects automatically, since it depends on what kind of window you're talking about. By default, views and frames destroy themselves automatically, whereas dialogs and controls (CButton, CListBox, and company) do not. In all cases, however, the place where MFC either does or does not delete the window object is in PostNcDestroy:

// view, frame delete themselves..
void CView::PostNcDestroy()
{
  delete this;
}
// ..but not most windows
void CWnd::PostNcDestroy()
{
}

WM_NCDESTROY is the last message Windows sends as it destroys a window. MFC's default handler CWnd:OnNcDestroy does a bunch of stuff, then at the very end calls the virtual function PostNcDestroy, the last function MFC calls as your window bites the dust. It's a little weird for a member function to delete its own object instance, but that's exactly what CView and CFrameWnd do. Hey, it works!

Why don't dialogs and other windows delete themselves too? By convention, MFC creates frames and views dynamically on the heap using operator new, so they must be deleted. By contrast, you normally create dialog objects on the stack, which means they're destroyed when they go out of scope. No need to call delete. Other windows like CButton and CListBox live as members of their parent object—view or dialog—and get destroyed along with it. Whether the parent lives on the stack or heap, child objects die with their parent.

So CDialog objects are not destroyed as part of WM_NCDESTROY processing. But since you normally create your CDialog on the stack, call DoModal and then return, CDialog objects are destroyed very soon after the dialog itself—but only because the dialog goes out of scope, not because MFC deletes it. You can take advantage of this feature to implement dialogs that remember their state across invocations by making your dialog static:

void CMyApp::OnFooCommand()
{
  static CFooDialog d; // one  and  only instance
  d.DoModal();
}
Here, your app runs and reruns the same CFooDialog dialog object every time the user invokes the Foo command. The CFooDialog is created only once, the first time OnFooCommand is invoked, and is not destroyed until your program terminates. Whatever values you store in CFooDialog's member data are preserved from one invocation to the next.

So the upshot of this long MFC digression is: while MFC doesn't always destroy window objects automatically, the standard conventions ensure that the window object is destroyed more or less immediately after the window, so the proper place to free resources is still your window object's destructor.

What about the .NET Framework? When a form is destroyed, does the .NET Framework automatically free the corresponding Form or Control? If memory is your main concern, the answer is: who cares? Sooner or later, the common language runtime (CLR) will free your form. It doesn't matter when, so just let the .NET Framework do its thing. But what if your Form holds unmanaged resources like open file handles, Internet connections, direct satellite uplinks, or other precious goodies you want to release as soon as your window goes bye bye? Remember, it's not polite to hog the satellite connection. So how do you make sure your resources get released ASAP?

In the .NET Framework, any time you have resources to release, you should immediately think: dispose. Dispose is the place to free stuff. The question then becomes, does the Framework automatically call Dispose when your window is destroyed? The short answer is yes, more or less. If you call Form.Close, the Framework automatically Disposes your form. But Application.Exit doesn't automatically close your form. So if you want to close it (and Dispose, too), you must explicitly call Close before Application.Exit:

// in, eg, Form1 class
private void OnAppExit(...)
{
  this.Close(); // close myself
}
This has the side effect of calling Dispose and freeing your resources. But where exactly does the .NET Framework call Dispose? More important, how can you find out?

There are many ways to go spelunking in the .NET Framework. Two of my favorites (because they're so easy) are trace diagnostics and ILDASM. Trace statements are particularly useful for revealing which virtual functions get called when. Just implement a bunch of overrides that spit out a message before calling your base class, then see what happens. Figure 1 shows a generic Form app sprinkled with tracing. It helps to indent your diagnostics information so you can see where in the call stack each message comes. I wrote a little class, TraceScopeIndenter, to automate indenting (see Figure 2). TraceScopeIndenter's constructor increments the indent; Dispose decrements it. To use TraceScopeIndenter, add a using statement around your function or code block, like so:

   void SomeFn()
   {
     using (new TraceScopeIndenter()) {
       •••  // tracing here gets indented
     } // compiler calls
       // TraceScopeIndenter.Dispose here
   }

Figure 2 TraceScopeIndenter (Close Figure 2)
//////////////////
// Use this class to indent all diagnostics within a particular
// scope--function or curly braces:
//
// using (new TraceScopeIndenter()) {
//   // trace statements here will be indented.
// }
//
// You can use Trace.IndentSize to set the number of spaces for each
// indent.
//
public class TraceScopeIndenter : IDisposable
{
    private bool disposed = false;

    // ctor: increment indent level
    public TraceScopeIndenter() {
        Trace.IndentLevel++;
    }

    // disposer: decrement
    public void Dispose()
    {
        if (!disposed) {
            Trace.IndentLevel--;
            disposed=true;
        }
    }
}
Figure 1 Generic Form with Tracing (Close Figure 1)
///////////////////////////////////////////////////////////////
// MSDN Magazine — May 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.
//
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using TraceWin;

////////////////////////////////////////////////////////////////
// This sample app does nothing more than reveal which virtual functions
// get called when a window is destroyed. If you want your Dispose
// method to get called when your form is destroyed, you must
// explicitly call Form.Close before exiting your app.
//
namespace FormApp
{
    //////////////////
    // Typical control, to see when it gets destroyed.
    //
    public class MyButton : Button
    {
        protected override void Dispose( bool disposing )
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine(String.Format("MyButton.Dispose({0})",
                    disposing));
            }
        }
        protected override void OnHandleDestroyed(EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("MyButton.OnHandleDestroyed");
                base.OnHandleDestroyed(e);
            }
        }
        ~MyButton()
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("MyButton.dtor");
            }
        }
    }

    // Typical form
    public class Form1 : System.Windows.Forms.Form
    {
        private MyButton button1;
        private bool disposed = false;
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            Trace.WriteLine("Initializing...");
            InitializeComponent();
        }

        ////////////////
        // Basic Dispose method frees all resources. Normally this will
        // be called eventually by the garbage collector; if you want it
        // called immediately when your window is destroyed, you must
        // close your window by calling Form.Close
        //
        protected override void Dispose( bool disposing )
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine(String.Format("Form1.Dispose({0}),
                                disposed={1}",
                    disposing,disposed));
                if (!disposed) {
                    if( disposing ) {
                        Trace.WriteLine("Form1 disposing");
                        if (components != null) {
                            components.Dispose();
                        }
                    }
                    disposed = true;
                    Trace.WriteLine("Form1 calling base.Dispose...");
                    base.Dispose(disposing); // calls Dispose for child
                                             // Controls
                    Trace.WriteLine("Form1 base.Dispose returned.");
                }
            }
        }

        #region Windows Form Designer generated code
        private void InitializeComponent()
        {
            this.button1 = new MyButton();
            this.SuspendLayout();
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(120, 176);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(80, 32);
            this.button1.TabIndex = 0;
            this.button1.Text = "&OK";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(208, 213);
            this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                   this.button1});
            this.Name = "Form1";
            this.Text = "Hello";
            this.ResumeLayout(false);

        }
        #endregion

        [STAThread]
        static void Main()
        {
            Debug.Listeners.Add(new TraceWinListener());
            Trace.IndentSize=1;
            Application.Run(new Form1());
            Trace.WriteLine("Form1 Calling Application.Exit...");
            Application.Exit();
            Trace.WriteLine("Form1 Application.Exit returned...");
        }

        // user clicked "OK" button
        private void button1_Click(object sender, System.EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1 OK clicked, calling this.Close...");
                this.Close();
            }
        }

        // This virtual fn is called when the window handle (HWND) is
        // destroyed.
        protected override void OnHandleDestroyed(EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.OnHandleDestroyed");
                base.OnHandleDestroyed(e);
            }
        }

        // This virtual fn is called when the window is closed (WM_CLOSE).
        // The base class implementation raises the Closed event. For
        // derived classes, you can handle the Closed event by overriding
        // this method instead of attaching a delegate. This is the
        // preferred way.
        //
        protected override void OnClosed(EventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.OnClosed");
                base.OnClosed(e);
            }
        }

        // This virtual fn is called before the window is closed. Same
        // remarks apply as for OnClosed. You can set
        // CancelEventArgs.Cancel=true to cancel closing the window.
        //
        protected override void OnClosing(CancelEventArgs e)
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.OnClosing");
                base.OnClosing(e);
            }
        }

        // This fn destroys the window handle (HWND). It calls
        // NativeWindow.DestroyHandle, which calls ::DestroyWindow.
        // This function is called from Control.Dispose. You must call
        // the base class here to actually destroy the handle (calls
        // ::DestroyWindow).
        //
        protected override void DestroyHandle()
        {
            using (new TraceScopeIndenter()) {
                Trace.WriteLine("Form1.DestroyHandle");
                base.DestroyHandle();
            }
        }

        protected override void WndProc(ref Message m)
        {
            using (new TraceScopeIndenter()) {
                // uncomment line below if you want to see all messages
                // Trace.WriteLine(String.Format("DefWndProc: {0}",m.Msg));
                if (m.Msg==2) {
                    // never called
                    Trace.WriteLine("Form1 got WM_DESTROY");
                } else if (m.Msg==16) {
                    // never called
                    Trace.WriteLine("Form1 got WM_CLOSE");
                } else if (m.Msg==130) {
                    // never called
                    Trace.WriteLine("Form1 got WM_NCDESTROY");
                }
                base.WndProc(ref m); // important!
            }
        }

        // This is the destructor (Finalize method). It should never get
        // called because Disposing an object removes it from the
        // Finalization queue. The CLR calls Finalize only if your object
        // fails to get Disposed.
        ~Form1()
        {
            Trace.WriteLine("Form1.dtor");
        }

    }
}
Figure 3 Before TraceScopeIndenter
Figure 3 Before TraceScopeIndenter

Figure 3 and Figure 4 show a typical trace dump before and after TraceScopeIndenter. A little indenting sure goes a long way to reveal what's going on! Once you have tracing, you can try various things and see what happens. For example, Figure 5 shows what happens if you don't call Form.Close before exiting your app: the .NET Framework does eventually dispose your form, but only after your application exits.

Figure 4 After TraceScopeIndenter
Figure 4 After TraceScopeIndenter

My other favorite spelunking tool is ILDASM. One of the best things about the .NET Framework is that it comes with full source! Well, not exactly. You don't get to see C# code with witty comments, but you can disassemble any .NET Framework class into Microsoft Intermediate Language (MSIL) as long as you know which DLL it lives in.

Figure 5 TraceWin
Figure 5 TraceWin

For example, to see what Form.Close does, open a console window, navigate to your framework directory, and type:

ildasm System.Windows.Forms.dll
You'll get a dialog like the one shown in Figure 6. You can expand the classes and double-click on any function—er, I mean method—to disassemble it. The code may look hieroglyphic, but you don't have to be an Egyptologist to get the gist. The highlighted lines in Figure 7 show that Form.Close simply calls SendMessage with uMsg = 16, which is WM_CLOSE. Most readers will be able to glean what's happening without a road map, but if you're curious, there's an ILDASM tutorial that comes with the MSDN® Library and a reference manual in \FrameworkSDK\Tool Developers Guide\docs\Partition III CIL.doc in your Visual Studio® .NET installation. Fellow columnist John Robbins' excellent May 2001 Bugslayer column provides a crib sheet for common MSIL instructions.

Figure 7 Form.Close in ILDASM (Close Figure 7)

Figure 6 ILDASM
Figure 6 ILDASM

Of course, the most surefire way to find out who calls your form's Dispose method is to add a breakpoint, run your application in the debugger, and examine the call stack when it croaks. Figure 8 shows the resulting screen.

Figure 8 Call Stack
Figure 8 Call Stack

With the combined efforts of trace diagnostics, ILDASM and the debugger, I can now state definitively what happens when your form is closed, starting when the user presses the OK (exit) button. First, the Framework calls the button's click handler, button1_Click. This in turn calls Form.Close. Form.Close sends a WM_CLOSE message to the form (see Figure 7). Form.WndProc catches the message and dispatches to a private method, Form.WmClose. WmClose does a bunch of stuff—including calling OnClosing and OnClosed to signal the Closing and Closed events—before finally calling Component.Dispose:

// Form.WmClose.
.method private void WmClose(...)
{
  ••• // lots of gnarly MSIL
  IL_0125:  ldarg.0 // "this" pointer
  IL_0126:  call instance void
    [System]System.ComponentModel.Component::Dispose()
  IL_012b:  ret
}

When I saw this I immediately thought, "Aha!" But just as quickly I was disturbed because the call to Component.Dispose is a direct call, not a callvirt, which means that control goes directly to System.ComponentModel.Component.Dispose, not my virtual override. But if you examine Component.Dispose (the [System] preceding the function name tells you it's in System.dll, not System.Windows.Forms.dll), you'll see that it calls the second, overloaded Dispose method, the one that takes a bool argument. And this time the call is virtual:

  // ComponentModel.Dispose
  .method public virtual void Dispose()
  {
    •••
    IL_0001:  ldc.i4.1 // True
    IL_0002:  callvirt instance void
     System.ComponentModel.Component::Dispose(bool)
    •••
  }
Whereupon control finally arrives at my Form1.Dispose—Whew!

That's probably way more than you ever wanted to know about window destruction in the .NET Framework, but at least now you have the wherewithal to go about investigating these mysteries yourself. If you do, you'll be richer for the experience. I gleaned several useful tidbits along the way to completing this exercise. First, there's no need to override OnClosed to Dispose of your resources, which is something I have seen many programmers do. All you have to do is free your resources in Dispose and remember to call Close. If you don't, overriding OnClosed has no effect because it won't be called.

Also of interest is Form.WndProc, the method that catches all Windows messages (WM_XXX). It's a giant switch dispatch to private methods WmActivate, WmClose, WmCreate, and so on. Ditto for other window objects like Control and ListView. So if you see a method WmWhatever, chances are good that it's the handler for WM_WHATEVER.

When I first started investigating, I guessed the form might be disposed in Form.OnClosed. But the methods OnClosing and OnClosed don't actually do anything except raise the corresponding Closing and Closed events. This is a general principle in Windows.Forms. Many events have a virtual method that does nothing but raise the event. This means that derived classes can handle the event by overriding the method instead of attaching a delegate. According to the official .NET Framework documentation, "this is the preferred technique for handling the event in a derived class." Just make sure that you call the base class method at the end to ensure the event gets raised for any other (nonderived) objects that may be listening.

Finally, as you go looking for functions to disassemble and learn from, you may discover that a particular class you're looking for is not in the location where you expect it to be. For example, System.ComponentModel.Component is in System.dll, not System.Win dows.Forms.dll. The tipoff is when you see a name in square brackets preceding the method name—for example [System] or [mscorlib]. This tells you which DLL to look in.

I uncovered one more mystery while exploring window destruction in the .NET Framework: I added a trace diagnostic to my form's destructor, but it never got called. This feels highly irregular to a longtime C++ programmer. If a destructor fails to get called you immediately think memory leak. But in the .NET Framework, the Finalize method (destructor in C#) is really only a backup in case an object fails to get Disposed. If an object is Disposed, there's no need to Finalize it—and, in fact, the last thing Component.Dispose does is call System.GC::SuppressFinalize on the object disposed to remove it from the Finalization queue. If you implement your own Dispose function for a class not derived from Component or any other class that implements Dispose, you should make sure to SuppressFinalize as well. Figure 9 shows the standard Dispose pattern. Happy programming!

Figure 9 Dispose Pattern (Close Figure 9)
///////////////////////////////////////////////////////////////////
// Dispose design pattern for base and derived classes

///////////////////
// Base class. Note that many .NET Framework classes already implement
// this stuff so all you have to do is implement the derived piece.
//
public class Base: IDisposable
{
   // implement IDisposable.
   public void Dispose()
   {
        Dispose(true);
      GC.SuppressFinalize(this);
   }

   protected virtual void Dispose(bool disposing)
   {
      if (disposing) {
         // free managed objects
      }
      // free unmanaged objects
      // set large fields to null (de-reference)
   }

   // Use C# destructor syntax for Finalize method code.
   ~Base()
   {
      Dispose (false); // simply call Dispose(false)
   }

//////////////////
// Derived class. The derived class does not have a Finalize method (C#
// dtor) or a Dispose method with no parameters because it inherits them
// from the base class.
//
public class Derived: Base
{
   protected override void Dispose(bool disposing)
   {
      if (disposing) {
         // free managed resources.
      }
      // Free unmanaged resources.
      // Set large fields to null.
      // Call base class Dispose.
      base.Dispose(disposing);
   }
}

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 http://www.dilascia.com.


Resource File: Windows Media 9 Series Digital Rights Management

Resource File
Windows Media 9 Series Digital Rights Management

On this page we provide information on the most current Microsoft developer resources. This month we introduce you to Digital Rights Management which is available through Windows Media 9 Series.

If you have an application that handles Windows Media content and you need an effective way to track content usage, Windows Media 9 Series now offers Digital Rights Management (DRM). It allows you to take advantage of the peer-to-peer distribution model and still redirect users back to your app once they have downloaded your content (prior to viewing). It also provides you with the ability to create an expiration date for your content.

The Windows Media 9 Series How-To page for Windows Media DRM provides information on all the functionality that DRM offers. Digital Rights Management can be implemented as an additional application layer to add security, tracking, and site redirection to your content regardless of where that content travels. This layer can also be completely transparent to your existing app, and there are several different ways you can implement it.

If you want users to have full access to content and use DRM only for tracking, you can use the technology to catch play "events" that are kicked off by the Windows Media Player. Each time your content is played by a user who is connected to the Internet, a message will be sent by the Windows Media Player to your DRM server. Your DRM server then records the play event, automatically responds with a license for content playback, and then plays the content. The user isn't even aware of the DRM process.

With a bit more integration work, you can tie the content security mechanism offered by DRM into your current user authorization mechanism, ensuring that your content is only viewed by authorized users. You can even tie the content to the individual PC that accesses your site, a process known as individualization.

The Windows Media 9 Series How-To page for Windows Media DRM documents a sample DRM application that easily integrates with your existing Web applications. This app can protect your content from unlicensed users and control its availability. The sample DRM app is a .NET Framework Web-based application that users can be redirected to for licensing. Upon licensing, users are redirected back to your original application. The entire trip usually takes less than a second, so users may not even notice that they have left your site.

You can control the kind of license that the sample DRM application provides to the user by giving licensing information to the sample DRM application as form variables associated with the redirection. The sample DRM application uses the submitted form variables to create the appropriate license, which is then stored in the user's license store through a client-side control (the license store and the delivery control are part of Windows Media Player version 7.0 and later).

This pre-licensing can be done any time prior to content playback. The sample DRM application also supports post-licensing, which occurs when a user attempts to play content that has not yet been licensed. When the Windows Media Player is asked to play a piece of content with DRM, it first checks the license store for an available license. If it fails to find the correct license, it opens up a mini-browser that accesses a licensing URL (which is embedded in the content header during DRM encryption). A full HTTP session is hosted in the mini-browser at this point, during which time your app can require the user to provide the credentials you require for playback. Once these are provided, your app can redirect the user to the sample DRM app for licensing. After a license has been delivered, the mini-browser enables a play button that closes the mini-browser and begins content playback.

The sample DRM application is available to licensed users of the Windows Media Digital Rights Management SDK. The Windows Media Licensing Form can be downloaded at http://wmlicense.smdisp.net/licenserequest/.