 MSDN Magazine January 2005

MSDN Magazine January 2005

ASP.NET: 10 Tips for Writing High-Performance Web Applications

Writing a Web application with ASP.NET is unbelievably easy. So many developers don't take the time to structure their applications for great performance. In this article, the author presents 10 tips for writing high-performance Web apps. The discussion is not limited to ASP.NET applications because they are just one subset of Web applications.

Rob Howard C++ Rules: Power Your App with the Programming Model and Compiler Optimizations of Visual C++

Many programmers think that C++ gets good performance because it generates native code, but even if your code is completely managed you'll still get superior performance. In Visual Studio 2005, the C++ syntax itself has been greatly improved to make it faster to write. In addition, a flexible language framework is provided for interacting with the common language runtime (CLR) to write high-performance programs. Read about it here.

Kang Su Gatlin Memory Lane: Rediscover the Lost Art of Memory Optimization in Your Managed Code

Managed applications rely on the garbage collector in the .NET Framework to allocate and clean up memory. The little CPU time spent performing garbage collection (GC) is usually a fair trade-off for not having to worry about memory management. But for applications in which CPU time and memory are precious resources, minimizing the time spent garbage collecting can greatly improve application performance and robustness. Find out how to manage memory all over again.

Erik Brown CLR Profiler: No Code Can Hide from the Profiling API in the .NET Framework 2.0

The common language runtime (CLR) profiling API makes available information about the application domains, assemblies, and classes that are loaded and used in a process, just-in-time (JIT) compiler notifications, memory usage tracking, tracing of events, exception tracking, managed to unmanaged code transitions, and the state of the runtime. And if that weren't enough, you will find a nicely enhanced profiling API in the .NET Framework 2.0. Find out what's coming up in this next version.

Jay Hilyard Interop: Get Seamless .NET Exception Logging From COM Clients Without Modifying Your Code

Using .NET objects from ASP can help you gain experience with the .NET Framework before migrating to ASP.NET. Your ASP pages will make use of the new .NET components through COM-callable wrappers (CCW). But how will you handle exceptions? This article fills you in.

Matt Adamson All About Statics: Get a Charge From Statics with Seven Essential Programming Tips

This article examines seven characteristics of statics that will help you in your development. The discussion will touch on static constructors and how the C# and Visual Basic compilers work together with the runtime to implement additional safety behind the scenes. By the end of the article, you will come away with best practices for the use of static members and static classes in your apps.

K. Scott Allen Editor's Note: Celebrating Our 20-Year Milestone

The world of software development is all about milestones. At the lowest level, a programmer has to create a certain set of functions in a fixed amount of time, and is then judged on this performance.

New Stuff: Resources for Your Developer Toolbox

Spices. VSIP. NET (Visual Studio Integration Pack), released by 9rays, is a set of plug-ins for Visual Studio® . NET that let you to add obfuscation, verification, modeling, and debugging to your assemblies built within the Microsoft® .

Marnie Hutcheson Web Q&A: Caching and Expiration, Connection Pools, and More

Edited by Nancy Michell Advanced Basics: Creating a Five-Star Rating Control

I have to admit it; most of my Windows® Forms controls are an attempt to copy something that already exists. In my October 2004 column I showed you how to create a progress bar that mimicked the one shown during the Windows XP setup routine, and this month I'm at it again.

Duncan Mackenzie Data Points: Data Source Controls in ASP.NET 2.0

ASP. NET 2. 0 introduces a series of new tools that improve data access including several data source and data bound controls. The new assortment of data source controls can eliminate a ton of repetitive code that was required in ASP.

John Papa Cutting Edge: Custom Script Callbacks in ASP.NET

ASP. NET client callbacks represent a neat and elegant way to execute server-side code without posting and refreshing the current page. I discussed ASP. NET callbacks in the August and December 2004 installments of Cutting Edge, considering them from the perspective of rendered pages making background callbacks to the server, sending input data to the relevant page, and receiving a response.

Dino Esposito Test Run: Lightweight UI Test Automation with .NET

Manual user interface testing is one of the most fundamental types of software testing and it's the kind of testing that most software engineers first experience. Paradoxically, automated user interface tests are probably the most technically challenging kind of test to write.

James McCaffrey The ASP Column: Determining Browser Capabilities in ASP.NET

Web applications are different from applications that run in homogenous environments because they send their output to all kinds of platforms and Web browsers. Some browsers support client-side scripting, some support XHTML, and still others have limited screen real estate.

George Shepherd Security Briefs: Security Enhancements in the .NET Framework 2.0

As I write this column, version 2. 0 of the Microsoft® . NET Framework is at Beta 1. When I got my bits, I hacked together a little program to dump all of the public members of all public types in the entire Framework and ran it on version 1.

Keith Brown .NET Matters: Sepia Tone, StringLogicalComparer, and More

Stephen Toub C++ Q&A: Sending Keystrokes to Any App, Calling .NET from an MFC App, and More

Paul DiLascia { End Bracket }: Joining the Team

It's been more than a year since my words last floated across the pages of MSDN®Magazine. As readers of my blog probably know, my life's been interesting the past 12 months.

Matt Pietrek


ASP.NET: 10 Tips for Writing High-Performance Web Applications

ASP.NET

10 Tips for Writing High-Performance Web Applications

Rob Howard
This article discusses:
  • Common ASP.NET performance myths
  • Useful performance tips and tricks for ASP.NET
  • Suggestions for working with a database from ASP.NET
  • Caching and background processing with ASP.NET
This article uses the following technologies:
ASP.NET, .NET Framework, IIS
Contents
Performance on the Data Tier
Tip 1—Return Multiple Resultsets
Tip 2—Paged Data Access
Tip 3—Connection Pooling
Tip 4—ASP.NET Cache API
Tip 5—Per-Request Caching
Tip 6—Background Processing
Tip 7—Page Output Caching and Proxy Servers
Tip 8—Run IIS 6.0 (If Only for Kernel Caching)
Tip 9—Use Gzip Compression
Tip 10—Server Control View State
Conclusion

W
riting a Web application with ASP.NET is unbelievably easy. So easy, many developers don't take the time to structure their applications for great performance. In this article, I'm going to present 10 tips for writing high-performance Web apps. I'm not limiting my comments to ASP.NET applications because they are just one subset of Web applications. This article won't be the definitive guide for performance-tuning Web applications—an entire book could easily be devoted to that. Instead, think of this as a good place to start.

Before becoming a workaholic, I used to do a lot of rock climbing. Prior to any big climb, I'd review the route in the guidebook and read the recommendations made by people who had visited the site before. But, no matter how good the guidebook, you need actual rock climbing experience before attempting a particularly challenging climb. Similarly, you can only learn how to write high-performance Web applications when you're faced with either fixing performance problems or running a high-throughput site.

My personal experience comes from having been an infrastructure Program Manager on the ASP.NET team at Microsoft, running and managing www.asp.net, and helping architect Community Server, which is the next version of several well-known ASP.NET applications (ASP.NET Forums, .Text, and nGallery combined into one platform). I'm sure that some of the tips that have helped me will help you as well.

You should think about the separation of your application into logical tiers. You might have heard of the term 3-tier (or n-tier) physical architecture. These are usually prescribed architecture patterns that physically divide functionality across processes and/or hardware. As the system needs to scale, more hardware can easily be added. There is, however, a performance hit associated with process and machine hopping, thus it should be avoided. So, whenever possible, run the ASP.NET pages and their associated components together in the same application.

Because of the separation of code and the boundaries between tiers, using Web services or remoting will decrease performance by 20 percent or more.

The data tier is a bit of a different beast since it is usually better to have dedicated hardware for your database. However, the cost of process hopping to the database is still high, thus performance on the data tier is the first place to look when optimizing your code.

Before diving in to fix performance problems in your applications, make sure you profile your applications to see exactly where the problems lie. Key performance counters (such as the one that indicates the percentage of time spent performing garbage collections) are also very useful for finding out where applications are spending the majority of their time. Yet the places where time is spent are often quite unintuitive.

There are two types of performance improvements described in this article: large optimizations, such as using the ASP.NET Cache, and tiny optimizations that repeat themselves. These tiny optimizations are sometimes the most interesting. You make a small change to code that gets called thousands and thousands of times. With a big optimization, you might see overall performance take a large jump. With a small one, you might shave a few milliseconds on a given request, but when compounded across the total requests per day, it can result in an enormous improvement.

Performance on the Data Tier

When it comes to performance-tuning an application, there is a single litmus test you can use to prioritize work: does the code access the database? If so, how often? Note that the same test could be applied for code that uses Web services or remoting, too, but I'm not covering those in this article.

If you have a database request required in a particular code path and you see other areas such as string manipulations that you want to optimize first, stop and perform your litmus test. Unless you have an egregious performance problem, your time would be better utilized trying to optimize the time spent in and connected to the database, the amount of data returned, and how often you make round-trips to and from the database.

With that general information established, let's look at ten tips that can help your application perform better. I'll begin with the changes that can make the biggest difference.

Tip 1—Return Multiple Resultsets

Review your database code to see if you have request paths that go to the database more than once. Each of those round-trips decreases the number of requests per second your application can serve. By returning multiple resultsets in a single database request, you can cut the total time spent communicating with the database. You'll be making your system more scalable, too, as you'll cut down on the work the database server is doing managing requests.

While you can return multiple resultsets using dynamic SQL, I prefer to use stored procedures. It's arguable whether business logic should reside in a stored procedure, but I think that if logic in a stored procedure can constrain the data returned (reduce the size of the dataset, time spent on the network, and not having to filter the data in the logic tier), it's a good thing.

Using a SqlCommand instance and its ExecuteReader method to populate strongly typed business classes, you can move the resultset pointer forward by calling NextResult. Figure 1 shows a sample conversation populating several ArrayLists with typed classes. Returning only the data you need from the database will additionally decrease memory allocations on your server.

Figure 1 Extracting Multiple Resultsets from a DataReader (Close Figure 1)
// read the first resultset
reader = command.ExecuteReader();

// read the data from that resultset
while (reader.Read()) {
    suppliers.Add(PopulateSupplierFromIDataReader( reader ));
}

// read the next resultset
reader.NextResult();

// read the data from that second resultset
while (reader.Read()) {
    products.Add(PopulateProductFromIDataReader( reader ));
}

Tip 2—Paged Data Access

The ASP.NET DataGrid exposes a wonderful capability: data paging support. When paging is enabled in the DataGrid, a fixed number of records is shown at a time. Additionally, paging UI is also shown at the bottom of the DataGrid for navigating through the records. The paging UI allows you to navigate backwards and forwards through displayed data, displaying a fixed number of records at a time.

There's one slight wrinkle. Paging with the DataGrid requires all of the data to be bound to the grid. For example, your data layer will need to return all of the data and then the DataGrid will filter all the displayed records based on the current page. If 100,000 records are returned when you're paging through the DataGrid, 99,975 records would be discarded on each request (assuming a page size of 25). As the number of records grows, the performance of the application will suffer as more and more data must be sent on each request.

One good approach to writing better paging code is to use stored procedures. Figure 2 shows a sample stored procedure that pages through the Orders table in the Northwind database. In a nutshell, all you're doing here is passing in the page index and the page size. The appropriate resultset is calculated and then returned.

Figure 2 Paging Through the Orders Table (Close Figure 2)
CREATE PROCEDURE northwind_OrdersPaged
(
    @PageIndex int,
    @PageSize int
)
AS
BEGIN
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int

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

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

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

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

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

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

END

In Community Server, we wrote a paging server control to do all the data paging. You'll see that I am using the ideas discussed in Tip 1, returning two resultsets from one stored procedure: the total number of records and the requested data.

The total number of records returned can vary depending on the query being executed. For example, a WHERE clause can be used to constrain the data returned. The total number of records to be returned must be known in order to calculate the total pages to be displayed in the paging UI. For example, if there are 1,000,000 total records and a WHERE clause is used that filters this to 1,000 records, the paging logic needs to be aware of the total number of records to properly render the paging UI.

Tip 3—Connection Pooling

Setting up the TCP connection between your Web application and SQL Server™ can be an expensive operation. Developers at Microsoft have been able to take advantage of connection pooling for some time now, allowing them to reuse connections to the database. Rather than setting up a new TCP connection on each request, a new connection is set up only when one is not available in the connection pool. When the connection is closed, it is returned to the pool where it remains connected to the database, as opposed to completely tearing down that TCP connection.

Of course you need to watch out for leaking connections. Always close your connections when you're finished with them. I repeat: no matter what anyone says about garbage collection within the Microsoft® .NET Framework, always call Close or Dispose explicitly on your connection when you are finished with it. Do not trust the common language runtime (CLR) to clean up and close your connection for you at a predetermined time. The CLR will eventually destroy the class and force the connection closed, but you have no guarantee when the garbage collection on the object will actually happen.

To use connection pooling optimally, there are a couple of rules to live by. First, open the connection, do the work, and then close the connection. It's okay to open and close the connection multiple times on each request if you have to (optimally you apply Tip 1) rather than keeping the connection open and passing it around through different methods. Second, use the same connection string (and the same thread identity if you're using integrated authentication). If you don't use the same connection string, for example customizing the connection string based on the logged-in user, you won't get the same optimization value provided by connection pooling. And if you use integrated authentication while impersonating a large set of users, your pooling will also be much less effective. The .NET CLR data performance counters can be very useful when attempting to track down any performance issues that are related to connection pooling.

Whenever your application is connecting to a resource, such as a database, running in another process, you should optimize by focusing on the time spent connecting to the resource, the time spent sending or retrieving data, and the number of round-trips. Optimizing any kind of process hop in your application is the first place to start to achieve better performance.

The application tier contains the logic that connects to your data layer and transforms data into meaningful class instances and business processes. For example, in Community Server, this is where you populate a Forums or Threads collection, and apply business rules such as permissions; most importantly it is where the Caching logic is performed.

Tip 4—ASP.NET Cache API

One of the very first things you should do before writing a line of application code is architect the application tier to maximize and exploit the ASP.NET Cache feature.

If your components are running within an ASP.NET application, you simply need to include a reference to System.Web.dll in your application project. When you need access to the Cache, use the HttpRuntime.Cache property (the same object is also accessible through Page.Cache and HttpContext.Cache).

There are several rules for caching data. First, if data can be used more than once it's a good candidate for caching. Second, if data is general rather than specific to a given request or user, it's a great candidate for the cache. If the data is user- or request-specific, but is long lived, it can still be cached, but may not be used as frequently. Third, an often overlooked rule is that sometimes you can cache too much. Generally on an x86 machine, you want to run a process with no higher than 800MB of private bytes in order to reduce the chance of an out-of-memory error. Therefore, caching should be bounded. In other words, you may be able to reuse a result of a computation, but if that computation takes 10 parameters, you might attempt to cache on 10 permutations, which will likely get you into trouble. One of the most common support calls for ASP.NET is out-of-memory errors caused by overcaching, especially of large datasets.Common Performance Myths

One of the most common myths is that C# code is faster than Visual Basic code. There is a grain of truth in this, as it is possible to take several performance-hindering actions in Visual Basic that are not possible to accomplish in C#, such as not explicitly declaring types. But if good programming practices are followed, there is no reason why Visual Basic and C# code cannot execute with nearly identical performance. To put it more succinctly, similar code produces similar results.

Another myth is that codebehind is faster than inline, which is absolutely false. It doesn't matter where your code for your ASP.NET application lives, whether in a codebehind file or inline with the ASP.NET page. Sometimes I prefer to use inline code as changes don't incur the same update costs as codebehind. For example, with codebehind you have to update the entire codebehind DLL, which can be a scary proposition.

Myth number three is that components are faster than pages. This was true in Classic ASP when compiled COM servers were much faster than VBScript. With ASP.NET, however, both pages and components are classes. Whether your code is inline in a page, within a codebehind, or in a separate component makes little performance difference. Organizationally, it is better to group functionality logically this way, but again it makes no difference with regard to performance.

The final myth I want to dispel is that every functionality that you want to occur between two apps should be implemented as a Web service. Web services should be used to connect disparate systems or to provide remote access to system functionality or behaviors. They should not be used internally to connect two similar systems. While easy to use, there are much better alternatives. The worst thing you can do is use Web services for communicating between ASP and ASP.NET applications running on the same server, which I've witnessed all too frequently.

Figure 3 ASP.NET Cache
Figure 3 ASP.NET Cache

There are a several great features of the Cache that you need to know. The first is that the Cache implements a least-recently-used algorithm, allowing ASP.NET to force a Cache purge—automatically removing unused items from the Cache—if memory is running low. Secondly, the Cache supports expiration dependencies that can force invalidation. These include time, key, and file. Time is often used, but with ASP.NET 2.0 a new and more powerful invalidation type is being introduced: database cache invalidation. This refers to the automatic removal of entries in the cache when data in the database changes. For more information on database cache invalidation, see Dino Esposito's Cutting Edge column in the July 2004 issue of MSDN®Magazine. For a look at the architecture of the cache, see Figure 3.

Tip 5—Per-Request Caching

Earlier in the article, I mentioned that small improvements to frequently traversed code paths can lead to big, overall performance gains. One of my absolute favorites of these is something I've termed per-request caching.

Whereas the Cache API is designed to cache data for a long period or until some condition is met, per-request caching simply means caching the data for the duration of the request. A particular code path is accessed frequently on each request but the data only needs to be fetched, applied, modified, or updated once. This sounds fairly theoretical, so let's consider a concrete example.

In the Forums application of Community Server, each server control used on a page requires personalization data to determine which skin to use, the style sheet to use, as well as other personalization data. Some of this data can be cached for a long period of time, but some data, such as the skin to use for the controls, is fetched once on each request and reused multiple times during the execution of the request.

To accomplish per-request caching, use the ASP.NET HttpContext. An instance of HttpContext is created with every request and is accessible anywhere during that request from the HttpContext.Current property. The HttpContext class has a special Items collection property; objects and data added to this Items collection are cached only for the duration of the request. Just as you can use the Cache to store frequently accessed data, you can use HttpContext.Items to store data that you'll use only on a per-request basis. The logic behind this is simple: data is added to the HttpContext.Items collection when it doesn't exist, and on subsequent lookups the data found in HttpContext.Items is simply returned.

Tip 6—Background Processing

The path through your code should be as fast as possible, right? There may be times when you find yourself performing expensive tasks on each request or once every n requests. Sending out e-mails or parsing and validation of incoming data are just a few examples.

When tearing apart ASP.NET Forums 1.0 and rebuilding what became Community Server, we found that the code path for adding a new post was pretty slow. Each time a post was added, the application first needed to ensure that there were no duplicate posts, then it had to parse the post using a "badword" filter, parse the post for emoticons, tokenize and index the post, add the post to the moderation queue when required, validate attachments, and finally, once posted, send e-mail notifications out to any subscribers. Clearly, that's a lot of work.

It turns out that most of the time was spent in the indexing logic and sending e-mails. Indexing a post was a time-consuming operation, and it turned out that the built-in System.Web.Mail functionality would connect to an SMTP server and send the e-mails serially. As the number of subscribers to a particular post or topic area increased, it would take longer and longer to perform the AddPost function.

Indexing e-mail didn't need to happen on each request. Ideally, we wanted to batch this work together and index 25 posts at a time or send all the e-mails every five minutes. We decided to use the same code I had used to prototype database cache invalidation for what eventually got baked into Visual Studio® 2005.

The Timer class, found in the System.Threading namespace, is a wonderfully useful, but less well-known class in the .NET Framework, at least for Web developers. Once created, the Timer will invoke the specified callback on a thread from the ThreadPool at a configurable interval. This means you can set up code to execute without an incoming request to your ASP.NET application, an ideal situation for background processing. You can do work such as indexing or sending e-mail in this background process too.

There are a couple of problems with this technique, though. If your application domain unloads, the timer instance will stop firing its events. In addition, since the CLR has a hard gate on the number of threads per process, you can get into a situation on a heavily loaded server where timers may not have threads to complete on and can be somewhat delayed. ASP.NET tries to minimize the chances of this happening by reserving a certain number of free threads in the process and only using a portion of the total threads for request processing. However, if you have lots of asynchronous work, this can be an issue.

There is not enough room to go into the code here, but you can download a digestible sample at www.rob-howard.net. Just grab the slides and demos from the Blackbelt TechEd 2004 presentation.

Tip 7—Page Output Caching and Proxy Servers

ASP.NET is your presentation layer (or should be); it consists of pages, user controls, server controls (HttpHandlers and HttpModules), and the content that they generate. If you have an ASP.NET page that generates output, whether HTML, XML, images, or any other data, and you run this code on each request and it generates the same output, you have a great candidate for page output caching.

By simply adding this line to the top of your page

<%@ Page OutputCache VaryByParams="none" Duration="60" %>
you can effectively generate the output for this page once and reuse it multiple times for up to 60 seconds, at which point the page will re-execute and the output will once be again added to the ASP.NET Cache. This behavior can also be accomplished using some lower-level programmatic APIs, too. There are several configurable settings for output caching, such as the VaryByParams attribute just described. VaryByParams just happens to be required, but allows you to specify the HTTP GET or HTTP POST parameters to vary the cache entries. For example, default.aspx?Report=1 or default.aspx?Report=2 could be output-cached by simply setting VaryByParam="Report". Additional parameters can be named by specifying a semicolon-separated list.

Many people don't realize that when the Output Cache is used, the ASP.NET page also generates a set of HTTP headers that downstream caching servers, such as those used by the Microsoft Internet Security and Acceleration Server or by Akamai. When HTTP Cache headers are set, the documents can be cached on these network resources, and client requests can be satisfied without having to go back to the origin server.

Using page output caching, then, does not make your application more efficient, but it can potentially reduce the load on your server as downstream caching technology caches documents. Of course, this can only be anonymous content; once it's downstream, you won't see the requests anymore and can't perform authentication to prevent access to it.

Tip 8—Run IIS 6.0 (If Only for Kernel Caching)

If you're not running IIS 6.0 (Windows Server™ 2003), you're missing out on some great performance enhancements in the Microsoft Web server. In Tip 7, I talked about output caching. In IIS 5.0, a request comes through IIS and then to ASP.NET. When caching is involved, an HttpModule in ASP.NET receives the request, and returns the contents from the Cache.

If you're using IIS 6.0, there is a nice little feature called kernel caching that doesn't require any code changes to ASP.NET. When a request is output-cached by ASP.NET, the IIS kernel cache receives a copy of the cached data. When a request comes from the network driver, a kernel-level driver (no context switch to user mode) receives the request, and if cached, flushes the cached data to the response, and completes execution. This means that when you use kernel-mode caching with IIS and ASP.NET output caching, you'll see unbelievable performance results. At one point during the Visual Studio 2005 development of ASP.NET, I was the program manager responsible for ASP.NET performance. The developers did the magic, but I saw all the reports on a daily basis. The kernel mode caching results were always the most interesting. The common characteristic was network saturation by requests/responses and IIS running at about five percent CPU utilization. It was amazing! There are certainly other reasons for using IIS 6.0, but kernel mode caching is an obvious one.

Tip 9—Use Gzip Compression

While not necessarily a server performance tip (since you might see CPU utilization go up), using gzip compression can decrease the number of bytes sent by your server. This gives the perception of faster pages and also cuts down on bandwidth usage. Depending on the data sent, how well it can be compressed, and whether the client browsers support it (IIS will only send gzip compressed content to clients that support gzip compression, such as Internet Explorer 6.0 and Firefox), your server can serve more requests per second. In fact, just about any time you can decrease the amount of data returned, you will increase requests per second.

The good news is that gzip compression is built into IIS 6.0 and is much better than the gzip compression used in IIS 5.0. Unfortunately, when attempting to turn on gzip compression in IIS 6.0, you may not be able to locate the setting on the properties dialog in IIS. The IIS team built awesome gzip capabilities into the server, but neglected to include an administrative UI for enabling it. To enable gzip compression, you have to spelunk into the innards of the XML configuration settings of IIS 6.0 (which isn't for the faint of heart). By the way, the credit goes to Scott Forsyth of OrcsWeb who helped me figure this out for the www.asp.net severs hosted by OrcsWeb.

Rather than include the procedure in this article, just read the article by Brad Wilson at IIS6 Compression. There's also a Knowledge Base article on enabling compression for ASPX, available at Enable ASPX Compression in IIS. It should be noted, however, that dynamic compression and kernel caching are mutually exclusive on IIS 6.0 due to some implementation details.

Tip 10—Server Control View State

View state is a fancy name for ASP.NET storing some state data in a hidden input field inside the generated page. When the page is posted back to the server, the server can parse, validate, and apply this view state data back to the page's tree of controls. View state is a very powerful capability since it allows state to be persisted with the client and it requires no cookies or server memory to save this state. Many ASP.NET server controls use view state to persist settings made during interactions with elements on the page, for example, saving the current page that is being displayed when paging through data.

There are a number of drawbacks to the use of view state, however. First of all, it increases the total payload of the page both when served and when requested. There is also an additional overhead incurred when serializing or deserializing view state data that is posted back to the server. Lastly, view state increases the memory allocations on the server.

Several server controls, the most well known of which is the DataGrid, tend to make excessive use of view state, even in cases where it is not needed. The default behavior of the ViewState property is enabled, but if you don't need it, you can turn it off at the control or page level. Within a control, you simply set the EnableViewState property to false, or you can set it globally within the page using this setting:

<%@ Page EnableViewState="false" %>
If you are not doing postbacks in a page or are always regenerating the controls on a page on each request, you should disable view state at the page level.

Conclusion

I've offered you some tips that I've found useful for writing high-performance ASP.NET applications. As I mentioned at the beginning of this article, this is more a preliminary guide than the last word on ASP.NET performance. (More information on improving the performance of ASP.NET apps can be found at Improving ASP.NET Performance.) Only through your own experience can you find the best way to solve your unique performance problems. However, during your journey, these tips should provide you with good guidance. In software development, there are very few absolutes; every application is unique.

See the sidebar "Common Performance Myths".

Rob Howard is the founder of Telligent Systems, specializing in high-performance Web apps and knowledge management and collaboration systems. Previously, Rob was employed by Microsoft where he helped design the infrastructure features of ASP.NET 1.0, 1.1, and 2.0. You can contact Rob at rhoward@telligentsystems.com.

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


C++ Rules: Power Your App with the Programming Model and Compiler Optimizations of Visual C++

C++ Rules

Power Your App with the Programming Model and Compiler Optimizations of Visual C++

Kang Su Gatlin
Parts of this article are based on a prerelease version of Visual Studio, formerly code-named "Whidbey." All information pertaining to this Beta is subject to change.
This article discusses:
  • Why C++ is the power language of .NET
  • How to get great performance programming with .NET in C++
  • The role of the C++ and JIT optimizer
  • Delay loading and STL/CLI
This article uses the following technologies:
C++, Visual Studio
Contents
Optimized MSIL
JIT and Compiler Optimization Interaction
Common Subexpression Elimination and Algebraic Simplification
Whole Program Optimization
64-bit NGEN Optimizations
Double Thunk Elimination
C++ Interop
Native and Managed Code in a Single Image
High-Performance Marshaling
Templates and STL with .NET Types
Determinism Helps Performance
Delay Loading
Why dllexport is Not Always a Good Idea
Conclusion

W
hile there is no question that the Microsoft® .NET Framework improves developer productivity, many people have some concern regarding the performance of managed code. The new version of Visual C++® will allow you to set aside these fears. For Visual Studio® 2005, the C++ syntax itself has been greatly improved to make it faster to write. In addition, a flexible language framework is provided for interacting with the common language runtime (CLR) to write high-performance programs.

Many programmers think that C++ gets good performance because it generates native code, but even if your code is completely managed you'll still get superior performance. With its flexible programming model, C++ doesn't tie you to procedural programming, object-oriented programming, generative programming, or meta-programming.

Another common misconception is that the same kind of superior performance on the .NET Framework can be attained regardless of the language you use—that the generated Microsoft intermediate language (MSIL) from various compilers is inherently equal. Even in Visual Studio .NET 2003 this was not true, but in Visual Studio 2005, the C++ compiler team went to great lengths to make sure that all of the expertise gained from years of optimizing native code was applied to managed code optimization. C++ gives you the flexibility to do fine tuning such as high-performance marshaling that is not possible with other languages. Moreover, the Visual C++ compiler generates the best optimized MSIL of any of the .NET languages. The result is that the best optimized code in .NET comes from the Visual C++ compiler.

Optimized MSIL

In the .NET environment, there are two distinct parts to compilation. The first part entails a programmer compiling and optimizing with the language compiler (C#, Visual Basic®, or Visual C++) to generate MSIL. The second part involves the MSIL being fed to the just-in-time (JIT) compiler or NGEN, which reads the MSIL and then generates optimized native machine code. Obviously, the language compiler and JIT are intertwined components, which means that to generate good code, they must cooperate.

Visual C++ has always offered the most advanced set of optimizations of any compiler. This has not changed in managed code. This is apparent even in Visual C++ .NET 2003, which just began enabling the optimizations from the native compiler for MSIL code generation.

With Visual C++ 2005, the compiler can perform a large subset of the standard native code optimizations on MSIL code. These include everything from dataflow-based optimizations to expression optimization to loop unrolling. This level of optimization is unmatched in any other language on the platform. In Visual C++ .NET 2003, Whole Program Optimization (WPO) was not supported for builds using the /clr switch, but Visual C++ 2005 adds this capability for managed code. This feature enables cross-module optimization, which I'll discuss later in the article.

The only class of optimizations not available for managed code in Visual C++ 2005 are Profile Guided Optimizations, although they may be available in a future version. For more information, see Write Faster Code with the Modern Language Features of Visual C++ 2005.

JIT and Compiler Optimization Interaction

The optimized code that Visual C++ generates is fed to the JIT or NGEN to generate native code. Whether the code generated by the Visual C++ compiler is MSIL or unmanaged code, the optimizer that generates the code is the same one that has been developed and tuned for over a decade.

The optimizations done on MSIL code are a large subset of those done on unmanaged code. It's worth pointing out that the class of allowable optimizations is different depending on whether the compiler is generating verifiable code (/clr:safe) or unverifiable code (/clr or /clr:pure). A few examples of the types of things that the compiler can not do because of either metadata or verifiability constraints include strength reduction (to convert multiplications into pointer addition), and inlining the access of private members of one class into the method body of another class.

After the MSIL code is generated by the Visual C++ compiler, it is then consumed by the JIT. The JIT reads in the MSIL and begins to perform optimizations that are very sensitive to changes in the MSIL. One instruction sequence of MSIL might be very amenable to optimization, whereas another (semantically equivalent) sequence stifles optimization. For example, register allocation is an optimization in which the JIT optimizer attempts to map variables to registers; registers are what the actual hardware uses as operands to perform arithmetic and logical operations. At times, semantically equivalent code, written in two different ways, might cause the optimizer to have a more difficult time performing good register allocation. Loop unrolling is an example of a transformation that might cause the JIT to have problems register-allocating.

Loop unrolling done by the C++ compiler can expose more instruction-level parallelism, but can also create more live variables that the optimizer needs to track for register allocation. The CLR JIT can only track a fixed number of variables for register allocation; once it has to track more than this, it begins to spill the contents of registers into memory.

For this reason, the Visual C++ compiler and the JIT must be tuned in tandem to generate the best code. The Visual C++ compiler is responsible for those optimizations that are too time-consuming for the JIT and those for which too much information would be lost in the compilation process from C++ source to MSIL.

Let's look now at some Visual C++ optimizations on managed code.

Common Subexpression Elimination and Algebraic Simplification

Common subexpression elimination (CSE) and algebraic simplification are two powerful optimizations that allow the compiler to perform some basic optimization at an expression level so that the developer can focus on algorithms and architecture.

The following shows a snippet of code compiled as C# and as C++ respectively; both were compiled with Release configurations. The variables a, b, and c were copied from an array passed as a parameter to the function containing the code:

int d = a + b * c;
int e = (c * b) * 12 + a + (a + b * c);
Figure 1 shows the MSIL generated from this code by the C# compiler and the C++ compiler, both with optimizations enabled. C# requires 19 instructions versus 13 for C++. Also, you can see that the C++ code has been able to do a CSE on the expression b*c. The optimizer has been able to do algebraic simplification on a+a, instead generating 2*a, and on (c*b)*12 + c*b, instead generating (c*b)*13. I find this latter CSE especially cool, as I've seen real programmers miss this sort of algebraic simplification in real code. See the sidebar "C# Compiler Optimizations".

Figure 1 Compiled Code Snippet (Close Figure 1)

MSIL generated by the C# compiler
// a is in local 0, b is in local 1, c is in
// local 2
1:  ldloc.0
2:  ldloc.1
3:  ldloc.2
4:  mul
5:  add
6:  stloc.3
7:  ldloc.2
8:  ldloc.1
9:  mul
10:  ldc.i4.s   12
11:  mul
12:  ldloc.0
13:  add
14:  ldloc.0
15:  ldloc.1
16:  ldloc.2
17:  mul
18:  add
19:  add
MSIL generated by the C++ compiler
// a is in local 1, b is in local 2, c is on the
// top of the stack but c is not in a local.
1:  ldloc.2            // b, c
2:  mul                // b*c
3:  stloc.0
4:  ldloc.0            // b*c
5:  ldloc.1            // a, b*c
6:  add                // a+b*c
7:  ldloc.1            // a, a+b*c
8:  ldc.i4.2           // 2, a, a+b*c
9:  mul                // 2*a, a+b*c
10:  ldloc.0           // b*c, 2*a, a+b*c
11:  ldc.i4.s   13     // 13, b*c, 2*a, a+b*c
12:  mul               // 13*b*c, 2*a, a+b*c
13:  add               // 13*b*c+2*a, a+b*c

Whole Program Optimization

WPO was added to unmanaged code in Visual C++ .NET. With Visual C++ 2005, this feature extends to managed code. Instead of compiling and optimizing one source file at a time, you compile and optimize across all of the source and header files at once.

The compiler can now perform analysis and optimization across multiple source files. Without WPO, for example, the compiler can only inline functions within a single compiland. With WPO, the compiler can inline functions from any source file in the program.

In the following example, the compiler can do things such as inlining and constant propagation across compilands, as well other types of interprocedural optimizations:

// Main.cpp
...
MSDNClass ^MSDNObj = gcnew MSDNClass;
int x = MSDNObj->Square(42);
return x;
...

// MSDNClass.cpp
int MSDNClass::Square(int x)
{
        return x*x;
}
In this example, Main.cpp calls the method Square, which is part of the MSDNClass in another source file. When compiled with /O2 optimizations, but without whole program optimization, the resulting MSIL in Main.cpp looks like the following:
ldc.i4.s   42
call       instance int32 MSDNClass::Square(int32)

You can see that the value 42 is loaded onto the stack and then the Square function is called. In contrast, here is the generated MSIL when the same program was compiled with whole program optimization turned on:

ldc.i4     0x6e4
There is no load of 42 and no call to the Square function. Instead, with Whole Program Optimization, the compiler was able to inline the function from MSDNClass.cpp and do constant propagation. The end result is simply one instruction that loads the result of 42*42, which in hex is 0x6e4.

While some of the analysis and optimization that is performed by the Visual C++ compiler is theoretically also possible with a JIT compiler, the time constraints that are imposed on a JIT compiler makes many of the optimizations mentioned here currently impractical to implement. In general, NGEN will get these types of optimizations before the JIT compiler does, because NGEN does not have the same type of response time restrictions that the JIT compiler has to contend with.

64-bit NGEN Optimizations

For the purposes of this article, I've referred to the JIT and NGEN together as the JIT. With the 32-bit version of the CLR, both the JIT compiler and NGEN perform the same optimizations. This is not the case for the 64-bit version, where NGEN does significantly more optimization than the JIT.

The 64-bit NGEN takes advantage of the fact that it can spend a lot more time compiling than a JIT can since the throughput of the JIT directly affects the response time of the application. I specifically mention the 64-bit NGEN in this article as it has been relatively well-tuned to work with C++-style code and it does some optimizations that greatly help C++, such as the Double Thunk Elimination optimization, which other JITs and NGENs do not do. The 32-bit JIT and the 64-bit JIT were implemented by two different teams within Microsoft using two different code bases. The 32-bit JIT was developed by the CLR team, whereas the 64-bit JIT was developed by the Visual C++ team and is based on the Visual C++ code base. Because the 64-bit JIT was developed by the C++ team, it is more aware of C++-related issues.

Double Thunk Elimination

One of the most important optimizations that the 64-bit NGEN performs is known as double thunk elimination. This optimization addresses a transition that happens when calling a managed entry point from managed code, through a function pointer or virtual call in C++ code compiled with the /clr switch. (This does not happen in /clr:pure or /clr:safe compiled code.) This transition occurs because neither the function pointer nor the virtual call have the necessary information at the callsite to determine if they're calling into a managed entry point (MEP) or an unmanaged entry point (UEP).

For backwards compatibility, the UEP is always selected. But what if the managed callsite is actually calling a managed method? In this case, there would be a thunk that goes from the UEP to the target managed method, in addition to the initial thunk from the managed callsite to the UEP. This managed-to-managed thunk process is what is commonly referred to as the double thunk.

64-bit NGEN optimizes this by realizing that the call to the "unmanaged to managed" is exactly that—a call that thunks back to managed code. There is a check that determines if this is the case; if so, it skips both thunks and jumps directly to the managed code, as shown in Figure 2. This saves many instructions and on real-world code-modeled benchmarks I've seen a factor of 5-10 ¥ improvement (on contrived tests I've seen over 100 ¥ performance improvement).

Figure 2 Double Thunk Elimination
Figure 2 Double Thunk Elimination

One thing that is worth noting, though, is that this optimization only kicks in when in the default application domain. A good rule of thumb is to remember that the default AppDomain will usually give better performance.C# Compiler Optimizations and Mike Montwill

The C# compiler contains a minimal set of optimizations to improve the generated MSIL. They can be turned on by specifying the /o+ flag on the command line passed to csc.exe or by the Optimize Code flag in the Build section of the Project Properties page in Visual Studio. When you turn on this option, you get a somewhat streamlined intermediate language instruction stream, which hopefully executes faster but is at the same time somewhat more difficult to debug.

The most obvious optimization is the removal of unused local variables. "Unused" means something very specific here. Basically, a local variable is unused if it is never read from in reachable code. Here's an example:

  {
      int a = foo(123);
      return;
      Console.WriteLine(a);
  }
If you try to debug a program containing this fragment after compiling with optimizations, the debugger will not be aware of the existence of the local variable, and the IL stream will contain the call to foo followed immediately by a POP instruction and the return sequence.

Another set of optimizations performed are best termed goto optimizations. They don't just apply to explicit goto statements you find in your code, but rather to IL branch instructions that are generated by the compiler, most frequently as a result of control flow statements such as if, switch, do, and while. Take for example, the following program fragment:

  if (b < 20) {
      Console.WriteLine("true");
  } else {
      goto After;
  }
  After:;
This would normally generate the following IL:
  IL_0003:  ldloc.0    // b is stored at location 0
  IL_0004:  ldc.i4.s   20
  IL_0006:  bge.s      IL_0014
  IL_0008:  ldstr      "true"
  IL_000d:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0012:  br.s       IL_0016
  IL_0014:  br.s       IL_0016
  IL_0016:  ret
But when optimizations are applied, you get:
  IL_0003:  ldloc.0    // b is stored at location 0
  IL_0004:  ldc.i4.s   20
  IL_0006:  bge.s      IL_0012
  IL_0008:  ldstr      "true"
  IL_000d:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0012:  ret

What basically happens is that branch instructions whose target is another (unconditional) branch instruction are retargeted to that second branch's target. Also, branches to the next instruction are eliminated.

The third kind of optimizations is somewhat more complicated. The following shows an example:

  if (!a) {
      goto C;
  }
  Console.WriteLine(3);
  C:
  Console.WriteLine(2);
This compiles to:
  IL_0002:  ldloc.0    // a is stored at location 0
  IL_0003:  brtrue.s   IL_0007
  IL_0005:  br.s       IL_000d
  IL_0007:  ldc.i4.3
  IL_0008:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000d:  ldc.i4.2
  IL_000e:  call       void [mscorlib]System.Console::WriteLine(int32)
But with optimizations, it turns into:
  IL_0002:  ldloc.0    // a is stored at location 0
  IL_0003:  brfalse.s  IL_000b
  IL_0005:  ldc.i4.3
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000b:  ldc.i4.2
  IL_000c:  call       void [mscorlib]System.Console::WriteLine(int32)
What happens here is that a conditional branch around an unconditional branch is reversed and retargeted to point at the target of the unconditional branch.

The next set of optimizations to perform are Try block optimizations. They are as follows:

  • A Try/Catch with no code inside of the Try is removed entirely.
  • A Try-Finally with no code in the Try is left completely alone. This is because the semantics of code executing in a Finally block and outside of it are different. Asynchronous ThreadAbort exceptions (those thrown by another thread) may interrupt the execution of all code, except for code found in Finally blocks.
  • A Try-Finally with an empty Finally block is converted to just the code in the Try block.
  • A Try/Catch-Finally is treated just like a Try/Catch nested inside a Try-Finally.

We now reach the category of optimizations that consist of removing IL constructs added to make debugging easier. These transformations are performed by default unless you specifically turn off optimizations, and turning optimizations off causes them not to be performed.

Let's look at return simplification. Normally, a simple return statement is compiled as follows:

  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_0004
  IL_0004:  ldloc.0
  IL_0005:  ret
With optimizations on, it becomes much simpler:
  IL_0000:  ldc.i4.0
  IL_0001:  ret
The reason for the complicated code in the first case is to associate the return with the closing brace of the method body such that a breakpoint in that location can always be hit at the end of the method.

The second of these optimizations you should consider is nop insertion. The new Visual C#® 2005 compiler will insert nops (the NOP, or no-operation instruction) in specific places to make debugging easier. When compiling with optimizations, these no-operation instructions will not be inserted. The list of possible locations may change before the final release of the product, but here are the possibilities we are currently working on:

  • Opening and closing braces of a block
  • The beginning of the foreach statement
  • After every call and property store statement
  • Other places, not normally visible to the user, but needed to generate IL that is compatible with Edit and Continue
The end effect of these extra instructions should be to make stepping under the debugger more useful and predictable.

The list of optimizations performed by the C# compiler itself is really short. We currently depend on the JIT compiler or NGEN to perform the classical optimizations such as common subexpression elimination or method inlining. The set of our optimizations consists of only those that could be performed with a minimum of effort and those that would be expected to cause no large performance impact. After all, it makes a lot more sense to invest in a really smart JIT compiler that can be applied to intermediate language generated by all the various language compilers. Also, my comments should not be construed as advice regarding which language constructs to prefer and which to avoid for any imaginary performance reasons. Code should be written first and foremost for clarity and then the hotspots should be optimized until performance targets are met.

C++ Interop

C++ interop is a technology for managed-native interop that allows standard C++ code compiled with the /clr switch to call native functions directly, without the programmer having to add any additional code. The code generated when using the /clr switch is MSIL (except for some rare exceptions), and the data can be managed or unmanaged (the user specifies where the data should go). I like to refer to C++ interop as the most important .NET feature that no one knows about. It's really borderline revolutionary, but it takes a while to realize exactly how much power C++ interop gives you.

In other .NET-compliant languages, doing interop with native code requires you to place the native code in a DLL and call the function with an explicit P/Invoke using dllimport (or something similar, depending on the language you are using). Otherwise the heavyweight COM interop must be used to access native code. This clearly is not as convenient, and often suffers from much worse performance than C++.

C++ interop is not typically thought of as a performance feature of the C++ language, but as you'll see, the flexibility and convenience offered by C++ interop allows you to get higher performance by targeting the CLR.

Native and Managed Code in a Single Image

Visual C++ gives the programmer the ability to selectively choose, on a function-by-function basis, which functions are managed and which are native. This is done with #pragma managed and #pragma unmanaged, an example of which is shown in Figure 3. In many computationally intensive tasks, it may make sense to have the core functions compiled native, while the rest of the code is compiled managed. C++ can mix, in a single image, both managed and native code, and calling managed functions from native functions (and vice-versa) requires no special syntax. With this degree of granularity, C++ makes it easy to control transitions for managed to native code, and vice versa.

Figure 3 Using #pragma unmanaged (Close Figure 3)
// The pragmas used here are enabled with /clr
#pragma unmanaged
int ComputeForces(Particles x) {
    // Computational intensive code, well suited for native
}
#pragma managed

int UpdateAndRender() {
    // Code to setup variables
    while(true)    {
        // Calling native code from managed.  Notice how it
        // looks just like calling any other function.
        ComputeForces(particleList);
        // Code to render particles
    }
}

When transitioning from managed to native code (or vice versa), the flow of execution goes through a compiler/linker-generated thunk. This thunk has a cost that programmers would obviously like to avoid. There was a lot of work done in the CLR and compilers to minimize the cost of this transition, but developers can also assist by minimizing the frequency of such transitions.

In part A of Figure 4, there is a C++ application where part of the code (Z.cpp) is compiled to generate MSIL (/clr) and the other parts (X.cpp and Y.cpp) are compiled to generate native code. In this program, there are functions in Y.cpp and Z.cpp that make many calls back and forth. This causes a lot of managed/native transitions, which can slow down program execution.

Figure 4 Changing Managed Boundaries
Figure 4 Changing Managed Boundaries

Part B of Figure 4 shows how you could optimize this program to minimize the managed/native transitions. The idea here is to identify the chatty interfaces and move them all to one side of the managed/native boundary, thereby eliminating all transitions across the chatty interface. Using the facilities that Visual C++ provides for interop, this is a simple task.

For example, to go from A to B in Figure 4, you only need to recompile Y.cpp with the /clr switch. Y.cpp is now compiled to managed code and calls from Z.cpp no longer incur the managed-to-native transition cost. Of course, you also need to consider the performance cost associated with Y.cpp being MSIL, and ensure that this is the right tradeoff for the application.

High-Performance Marshaling

Marshaling is one of the most costly aspects of managed/native interop. With languages such as C# and Visual Basic .NET, the marshaling is done implicitly by the CLR when P/Invoke is invoked (either using the default marshaler or with custom marshaling code when implementing ICustomMarshaler). With C++ interop, the programmer explicitly marshals data in her code wherever she deems appropriate. The great thing about this is that the programmer can marshal data once to native data, and then reuse the result of this marshaling of data over several calls, thereby amortizing the cost of marshaling.

Figure 5 shows a snippet that is compiled with the /clr switch. In this code there is a for-loop where a native function (GetChar) is called. In Figure 6, the same code is implemented in C# and the call to GetChar results in the CSharpType class being marshaled to a NativeType, as shown in the following:

class NATIVECODE_API NativeType {
public:
    NativeType();
    int pos;
    int length;
    char *theString;
};
In C++, the user explicitly uses a native type, thus not requiring the implicit marshaling. The cost savings for this type of optimization can be quite large. In this example, the C++ implementation was about 18 times faster than the C# implementation.

Figure 6 Functionally Equivalent C# Code (Close Figure 6)
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class CSharpType
{
    public int pos;
    public int length;
    public String str;

    public CSharpType()
    {
        pos = 0;
        str = "TheStartingString";
        length = str.Length;
    }
};

class MarshalExample
{
    [DllImport("NativeCode.dll",
        CallingConvention=CallingConvention.Cdecl,
        EntryPoint = "?GetChar@@YAHPAVNativeType@@@Z")]
    extern static public int GetChar(
        [In, MarshalAs(UnmanagedType.LPStruct)]CSharpType theData);

    [DllImport("Kernel32.dll")]
    private static extern bool QueryPerformanceCounter(
        out long lpPerformanceCount);

    [DllImport("Kernel32.dll")]
    private static extern bool QueryPerformanceFrequency(
        out long lpFrequency);

    public static void Main()
    {
        long start, stop, freq;

        CSharpType theCSharpData = new CSharpType();
        char theChar = ' ';
        QueryPerformanceFrequency(out freq);
        QueryPerformanceCounter(out start);
        for (int i = 0; i < 1000000; ++i)
        {
            theCSharpData.pos = i % theCSharpData.length;
            theChar = (char)GetChar(theCSharpData);
        }
        QueryPerformanceCounter(out stop);
        double time = (double)(stop - start) / (double)freq;
        Console.WriteLine(theChar);
        Console.WriteLine("{0}", time);
    }
}
Figure 5 Optimizing Marshaling Costs with Native C++ Types (Close Figure 5)
#include "stdafx.h"
#include <windows.h>
#include "NativeCode.h"

using namespace System;
using namespace System::Runtime::InteropServices;

public ref class CppType
{
public:
    int pos;
    int length;
    String ^str;

    CppType()
    {
        pos = 0;
        str = gcnew String("TheStartingString");
        length = str->Length;
    }
};

int main()
{
    LARGE_INTEGER start, stop, freq;

    NativeType *theNativeData = new NativeType;
    CppType ^theCppData = gcnew CppType;
    char theChar = ' ';
    QueryPerformanceFrequency(&freq);
    QueryPerformanceCounter(&start);

    // We copy the data from the managed type to the
    // native type
    theNativeData->pos = theCppData->pos;
    theNativeData->length = theCppData->length;
    theNativeData->theString =
         (char*)(void*)Marshal::StringToHGlobalAnsi(theCppData->str);

    for (int i = 0; i < 1000000; ++i)
    {
        theNativeData->pos = i % theNativeData->length;
        theChar = (char)GetChar(theNativeData);
    }
    QueryPerformanceCounter(&stop);
    double time = (double)(stop.LowPart - start.LowPart) /
         (double)freq.LowPart;
    Console::WriteLine("{0}", theChar);
    Console::WriteLine("{0}", time);
}

Templates and STL with .NET Types

Some of the more interesting new performance features in Visual C++ 2005 are templates with managed types (including STL/CLI), whole-program optimization with managed code, delay loading, and deterministic finalization. Visual C++ .NET 2003 is able to generate MSIL for templates on native types, but cannot use managed types as the parameterized type in the template. In Visual C++ 2005, this has been rectified, and now templates can have managed or unmanaged types as parameters. The incredible usefulness of templates now becomes available to code written within .NET (you should also make a point of seeing the work done by the Blitz++ and Boost libraries).

The C++ Standard Template Library (STL) was one of the great innovations in library design. It proved that you can get great abstractions with containers and algorithms without a performance hit. In Visual C++ .NET 2003, the restriction on managed types in templates meant that there was no counterpart of the STL for managed types. With this restriction lifted in Visual C++ 2005, STL/CLI, a verifiable version of the STL which works with managed types, was also introduced. The base class library (BCL) in .NET originally brought containers to .NET, but the plan in the Visual C++ group is that the performance of STL/CLI will be superior. If you want more info on STL/CLI, there is an excellent article by Stan Lippman in the Visual C++ Developer Center, available at STL.NET Primer.

With STL/CLI, you get implementations of all your favorites from STL, including vectors, lists, deques, maps, sets, and hash maps and sets. You also get algorithms for sorting, searching, set operations, inner_products, and accumulate. One of the surprising things about the STL/CLI algorithms is that the same implementation is used for native and the STL/CLI version. The good design of STL will benefit every C++ programmer in the form of portable and powerful code.

Determinism Helps Performance

It's easier to write efficient C++ thanks to the great library of patterns and idioms at your disposal. Many of these, including Resource Acquisition Is Initialization (RAII), use a feature of the C++ language that is known as deterministic finalization. This is the principle whereby the destructor of an object is invoked when the object is either deleted by the delete operator (for heap allocated objects) or goes out of scope (for stack allocated objects). Deterministic finalization aids performance because the longer an object holds a resource (beyond what is absolutely necessary), the more performance will be degraded as other objects try to acquire the same resource.

Using a CLR finalizer results in the finalizer code being executed at some point after the object is out of scope (assuming that the code to release the lock is the finalizer), but not until the finalization thread is invoked on the object's finalizer. Clearly, this is not ideal because the finalization thread may not execute when the programmer expects. Additionally, the memory associated with the object is not garbage collected until after the finalizer is executed, which can increase the memory demands on the program.

One of the common idioms used to help avoid this problem in .NET-based code is the Dispose pattern. To use it, the developer implements a Dispose method for their class, which he then calls when the object is no longer needed. This is called at the same point in code when a C++ programmer would invoke delete on an object, but even in C++ this is error prone and overly verbose. Languages such as C# have added the "using" construct, which helps the latter two problems, but again for nontrivial cases it is complex and error prone.

In contrast, the C++ idiom of RAII automatically acquires and releases resources and is less error prone as the programmer needn't write any additional code. In Visual C++ .NET 2003, deterministic finalization of stack-allocated .NET objects was not supported, but in Visual C++ 2005 this is a supported feature.

In of the top half of Figure 7, notice that the objects of type Socket_t use stack-based syntax and will have stack-based cleanup semantics. So, what if an exception is thrown on the third line? With the stack-based semantics, the destructor is run for mainSock deterministically, but since backupSock has not been created on the stack yet, there is no object to destruct.

Figure 7 Deterministic Finalization (Close Figure 7)

C++
int CheckPort(int p) {
    Socket_t mainSock(p);            // constructor run
    if(mainSock.Check()) return 1;
    Socket_t backupSock(p+1);        // constructor run
    if(backupSock.Check()) return 2;
    return 0;
}
C#
int CheckPort(int p) {
    using(Socket_t mainSock = new Socket_t(p)) {
        if(mainSock.Check()) return 1;
        using(Socket_t backupSock = new Socket_t(p+1)) {
            if(backupSock.Check()) return 2;
        }
    }
    return 0;
}

To write code that is semantically equivalent in C# would be a bit more tedious and error-prone; see the bottom half of Figure 7. Of course, this is a trivial example, but as the complexity of such a task increases, so does the likelihood of mistakes.

Delay Loading

Although the .NET Framework has been tuned for performance, there is a small delay during startup as the CLR loads. In many applications, there may be some code paths and scenarios, especially when retrofitting existing legacy programs with .NET functionality, where there is no managed code. In these situations, your application should not have this associated startup delay. You can solve this problem using an existing feature in Visual C++ known as delay loading of DLLs.

The idea is that you only load a DLL when you actually use something from the DLL. The same concept applies to loading DLLs that are .NET assemblies. By using the linker option /DELAYLOAD:dll, where you specify the .NET assemblies you want to delay load, in addition to delaying the loading of the listed .NET assemblies, you can delay the loading of the CLR (if all of the .NET assemblies are delay loaded). The end result is an application that launches as fast as it did when it was all native, thereby eliminating one of the most common complaints about managed apps.

Why dllexport is Not Always a Good Idea

Using __declspec(dllexport) has its drawbacks. The problem with dllexport occurs when you have two images (DLL or exe) that are both managed, but are exposing functionality via dllexport, rather than via #using. Because dllexport is a native construct, every call across the DLL boundary using __declspec(dllexport) will incur a managed-to-native transition, and then a native-to-managed transition. This won't provide great performance.

The options for correcting this performance issue are limited. There is no simple "switch" that suddenly makes __declspec(dllexport) a construct with no associated thunk going to managed code. The recommended fix is to wrap the exported functionality in a managed type (a ref or value class/struct), which the importer can then access via "#using" on the exporting DLL to directly access the functionality in the exporting DLL. With that change, there are no transitions when calling this managed code from a managed client. This is shown in Figure 8, where part A shows the cost associated with using __declspec(dllexport), and part B shows the optimization resulting from the use of #using and wrapping the functions in .NET types. A potential problem with this approach is that unmanaged importers of the exporting DLL will not be able __declspec(dllimport) the functionality of the DLL. This should be taken into consideration before making this change.

Figure 8 Reducing Thunk Costs
Figure 8 Reducing Thunk Costs

The A section of Figure 8 shows the transition path of using __declspec(dllexport) to expose managed functions to managed code. In section B, the function is wrapped in a managed type, and #using is used to access the function. The result is a removal of the costly thunks, as compared to the process in section A.

Conclusion

The .NET Framework with Visual C++ has come a long way since its introduction in Visual Studio .NET 2002. C++ gives the programmer unprecedented flexibility to write high-performance managed code, and it does it all in a way that is natural to C++ programmers. There are many languages available for .NET-enabled programming; if you care about maximum performance, Visual C++ is the obvious choice.

Kang Su Gatlin is a Program Manager on the Visual C++ team at Microsoft where he spends most of his day trying to figure out systematic ways to make programs run faster. Prior to his life at Microsoft, he worked on high-performance and grid computing.


Memory Lane: Rediscover the Lost Art of Memory Optimization in Your Managed Code

Memory Lane

Rediscover the Lost Art of Memory Optimization in Your Managed Code

Erik Brown
This article discusses:
  • How object types influence memory usage
  • How object pooling affects garbage collection
  • Data streaming when accessing large amounts of data
  • Memory utilization analysis
This article uses the following technologies:
.NET Framework, C#
Code download available at: MemoryOptimization.exe (136KB)
Contents
Type Sizing
Singletons
Pooling
Data Streaming
Performance Monitoring
CLR Profiler
Conclusion

M
emory is the one resource all programs require, yet wise memory usage is becoming a lost art. Managed applications written for the Microsoft® .NET Framework rely on the garbage collector to allocate and clean up memory. For many applications, the three to five percent of CPU time spent performing garbage collection (GC) is a fair trade-off for not having to worry about memory management.

But for applications in which CPU time and memory are precious resources, minimizing the time spent garbage collecting can greatly improve application performance and robustness. If an application can more effectively use available memory, then it stands to reason that the garbage collector would run less often and for shorter periods of time. So rather than looking at what the garbage collector is or is not doing in your applications, take a look at memory usage directly.

Most production machines have a huge amount of RAM, and in the grand scheme of things, optimizations such as using short integers instead of regular ones may seem a little pointless. In this article I'll change your mind. I will look at type sizing, various design techniques, and how to analyze a program's memory utilization. My examples will focus on C#, but the discussion applies equally to Visual Basic® .NET, managed C++, and any other .NET-targeted languages you can think of.

I assume you understand the basics of how garbage collection works, including related concepts like generations, the disposal pattern, and weak references. If you are not familiar with these concepts, Jeffrey Richter has a good article on garbage collection at Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework.

Type Sizing

Memory usage ultimately depends on the types defined and used by the assemblies in your program, so let's begin by examining the size of various types in the system.

Figure 1 shows the size in bytes of the core .NET value types defined in the System namespace, along with their equivalent C# types. I used unsafe code and the C# sizeof operator to verify the size of these value types in managed memory. Using the Marshal.SizeOf method instead of the sizeof operator will result in different values for some of these types, including bool and char, since Marshal.SizeOf computes the unmanaged size of a marshaled type, and these types are non-blittable (meaning that they may require conversion when passed between managed and unmanaged code). More on this in a moment.

Figure 1 .NET Value Types (Close Figure 1)

System Type | C# Type | Managed Size in Bytes | Default Unmanaged Size in Bytes
System.Boolean | bool | 1 | 4
System.Byte | byte | 1 | 1
System.Char | char | 2 | 1
System.Decimal | decimal | 16 | 16
System.Double | double | 8 | 8
System.Single | float | 4 | 4
System.Int16 | short | 2 | 2
System.Int32 | int | 4 | 4
System.Int64 | long | 8 | 8
System.SByte | sbyte | 1 | 1
System.UInt16 | ushort | 2 | 2
System.UInt32 | uint | 4 | 4
System.UInt64 | ulong | 8 | 8

The size of a structure (a value type) is computed as the sum of the sizes of its fields, plus any overhead added by aligning these fields to their natural boundaries. The size of a reference type is the size of its fields rounded up to the next 4-byte boundary, plus 8 bytes of overhead. (To get a sense for how much space is being used by your reference types, you can measure the change in heap size when allocating them, or you can use the CLR Profiler tool discussed later.) This means that all reference types occupy at least 12 bytes, so any object fewer than 16 bytes long might be more efficient as a structure in C#. Of course, a structure is problematic if you need to store a reference to the type, since frequent boxing can eat up both memory and CPU cycles. It's important, then, to use structures with care.

Since field alignment can influence the size of a type, field organization within a type plays a significant role in its ultimate size. The layout of a type and the organization of the fields with that layout are influenced by the StructLayoutAttribute applied to a type. By default, the C#, Visual Basic .NET, and C++ compilers all apply a StructLayoutAttribute to structures, specifying a Sequential layout. This means that the fields are laid out in the type according to their order in the source file. However, in the .NET Framework 1.x, a request for a Sequential layout is not respected by the just-in-time compiler (JIT), though it is by the marshaler. In the .NET Framework 2.0, the JIT does enforce a Sequential layout (if specified) for the managed layout of value types, though only if there are no reference type field members. Thus, the sizing of types is probably more important in the next version of the Framework. In all versions, a request for an Explicit layout (where you as the developer specify the field offsets for each and every field) is respected by both the JIT and by the marshaler.

I make this distinction because the marshaled layout of a type is typically not the same as the stack or GC heap layout of that type. A marshaled type's layout must match that of its unmanaged counterpart. The managed layout, however, is only used from managed code compiled by the JIT. So the JIT is able to optimize the managed layout based on the current platform without concern for external dependencies.

Consider the following C# structure (for simplicity, I have avoided specifying any access modifier for these members):

struct BadValueType
{
    char c1;
    int i;
    char c2;
}
As with the default packing in unmanaged C++, integers are laid out on four-byte boundaries, so while the first character uses two bytes (a char in managed code is a Unicode character, thus occupying two bytes), the integer moves up to the next 4-byte boundary, and the second character uses the subsequent 2 bytes. The resulting structure is 12 bytes when measured with Marshal.SizeOf (it's also 12 bytes when measured with sizeof on the .NET Framework 2.0 running on my 32-bit machine). If I reorganize this as follows, the alignment works in my favor, resulting in an 8-byte structure:
struct GoodValueType
{
    int i;
    char c1;
    char c2;
}

Another noteworthy point is that smaller types use less memory. That may seem obvious, but many projects use standard integers or decimal values even when they are unnecessary. In my GoodValueType example, assuming the integer values will never be greater than 32767 or less than -32768, I can cut the size of this type even further by using a short integer, as shown in the following:

struct GoodValueType2
{
    short i;
    char c1;
    char c2;
}

Properly aligning and sizing this type reduced it from 12 to 6 bytes. (Marshal.SizeOf will report 4 bytes for GoodValueType2, but that's because the default marshaling for a char is as a 1 byte value.) You will be surprised how much the size of your structures and classes can be reduced if you pay attention.

As mentioned, it's very important to realize that the managed layouts of structures can greatly differ from unmanaged layouts, especially on the .NET Framework 1.x. The marshaled layout could be different from the internal layout, so it's possible (and, in fact, probable) that the types I've described will report different results when using the sizeof operator. As a case in point, all three structures I've shown so far have a managed size of 8 bytes on the .NET Framework 1.x. You can examine the layout of one of these types by the JIT using unsafe code and pointer arithmetic:

unsafe
{
    BadValueType t = new BadValueType();
    Console.WriteLine("Size of t: {0}", sizeof(BadValueType));
    Console.WriteLine("Offset of i:  {0}", (byte*)&t.i - (byte*)&t);
    Console.WriteLine("Offset of c1: {0}", (byte*)&t.c1 - (byte*)&t);
    Console.WriteLine("Offset of c2: {0}", (byte*)&t.c2 - (byte*)&t);
}
On the .NET Framework 1.x, running this code results in the following output:
Size of BadValueType: 8
Offset of i:  0
Offset of c1: 4
Offset of c2: 6
Whereas on the .NET Framework 2.0, the same code will result in this output:
Size of BadValueType: 12
Offset of i:  4
Offset of c1: 0
Offset of c2: 8
While it may seem like a regression for the newer version of the Framework to increase the size of the type, it's actually expected behavior and a good thing that the JIT now respects the specified layout. If you'd prefer to let the JIT determine the best layout automatically (resulting in the same output as is currently generated by the 1.x JIT), you can explicitly mark your structure with a StructLayoutAttribute, specifying LayoutKind.Auto. Just keep in mind that for purely managed applications running on the .NET Framework 1.x that don't do any interop with unmanaged code, the savings you're trying to achieve by doing manual ordering of fields to get better alignment could be elusive.

Figure 2 illustrates some additional considerations. The Address class shown represents a United States address. This type is 36 bytes long: 4 bytes for each member, plus 8 for its reference type overhead (note that the sizeof operator in C# is only for value types, so again I'm relying on values as reported by Marshal.SizeOf). A large medical application that manages payments to doctors and hospitals might need to handle thousands of addresses simultaneously. In this case, minimizing the size of this class could be important. The ordering within the type is okay, but consider AddressType (see Figure 2).

Figure 2 AddressType (Close Figure 2)
enum AddressType
{
  Home,
  Secondary,
  Office
}

class Address
{
  public bool IsPayTo;
  public AddressType
    AddressType;
  public string Address1;
  public string Address2;
  public string City;
  public string State;
  public string Zip;
}

While enumerations are stored as integers by default, you can specify the integral base type to use. Figure 3 defines the AddressType enumeration as a short. By changing the IsPayTo field to byte as well, I have reduced the unmanaged size of each Address instance by more than 10 percent, from 36 to 32 bytes, and reduced the managed size by at least 2 bytes.

Figure 3 Reducing Type Size (Close Figure 3)
enum AddressType : short
{
    Home,
    Secondary,
    Office
}

class Address
{
    byte _isPayTo;
    AddressType _addrType;
    string _address1;
    string _address2;
    string _city;
    string _state;
    string _zip;

    public bool IsPayTo
    {
        get { return (_isPayTo == 1); }
        set { _isPayTo = (byte)(value ? 1 : 0); }
    }

    public string State
    {
        get { return _state; }
        set
        {
            if (value == null) _state = null;
            else _state = String.Intern(value.ToUpper());
        }
    }

    public AddressType AddressType
    {
        get { return _addrType; } set { _addrType = value; }
    }

    public string Address1
    {
        get { return _address1; } set { _address1 = value; }
    }

    public string Address2
    {
        get { return _address2; } set { _address2 = value; }
    }

    public string City { ... }

    public string Zip { ... }
}

Finally, the string type is a reference type, so every string instance refers to an additional block of memory to hold the actual string data. In the Address type, if I ignore the various U.S. territories, then the state field has 50 possible values. It might be worth considering an enumeration here since it would remove the need for a reference type and store the value directly in the class. The base type for the enumeration could be a byte rather than the default int, resulting the field requiring 1 byte rather than 4. While this is a viable alternative, it does complicate data display and storage because the integer value would have to be converted into something a user or storage mechanism understands each time it is accessed or stored. This situation brings to light one of the more common trade-offs in computing: speed versus memory. It is often possible to optimize memory usage at the expense of some CPU cycles, and vice versa.

An alternate option here is to use interned strings. The CLR maintains a table called the intern pool that contains the literal strings in a program. This ensures that repeated use of the same constant strings in your code will utilize the same string reference. The System.String class provides an Intern method that ensures a string is in the intern pool and returns the reference to it. This is illustrated in Figure 3.

Before I conclude the discussion of the sizing of types, I also want to mention base classes. The size of a derived class is the size of the base class plus the additional members defined by a derived instance (and any extra space required for alignment, as discussed earlier). As a result, any base field that is not used in the derived type is a waste of good memory. A base class is great for defining common functionality, but you must make sure that each data element defined is truly necessary.

Next, I'll discuss some design and implementation techniques for efficient memory management. The memory an assembly requires depends largely on what the assembly does, but the memory actually used by an assembly is affected by how an application goes about its various tasks. This is an important distinction to keep in mind when designing and implementing an application. I'll examine the idea of a singleton, memory pooling, and data streaming.

Singletons

The working set of an application is the set of memory pages currently available in RAM. The initial working set is the memory pages the application consumes during startup. The more tasks performed and memory allocated during application startup, the longer before an application is ready and the larger the initial working set. This is especially important for desktop applications, where a user is often staring at the splash screen waiting for the application to be ready.

The singleton pattern can be used to delay the initialization of an object as long as possible. The following code shows one way to implement this pattern in C#. A static field holds the singleton instance, which is returned by the GetInstance method. The static constructor (implicitly generated by the C# compiler to execute all of the static field initializers) is guaranteed to execute before the first access to a member of the class and initializes the static instance, as shown in the following code:

public class Singleton
{
    private static Singleton _instance = new Singleton();

    public static Singleton GetInstance()
    {
        return _instance;
    }
}

The singleton pattern ensures that only a single instance of a class is normally used by an application, but still allows alternate instances to be created as required. This saves memory because the application can use the one shared instance, rather than having different components allocate their own private instances. Use of the static constructor ensures that memory for the shared instance is not allocated until some portion of the application requires it. This might be important in large applications that support many different types of functionality, since the memory for the object is only allocated if the class is actually used.

This pattern and similar techniques are sometimes called lazy initialization, since the initialization is not performed until actually required. Lazy initialization is quite useful in a number of scenarios when the initialization can occur as part of the first request to an object. It should not be used where static methods would suffice. In other words, if you're creating a singleton to have access to a bunch of instance members of that singleton class, consider whether it would make more sense to expose the same functionality through static members, as that would not require you to instantiate the singleton.

Pooling

Once an application is up and running, memory utilization is affected by the number and size of objects the system requires. Object pooling reduces the number of allocations, and therefore the number of garbage collections, required by an application. Pooling is quite simple: an object is reused instead of allowing it to be reclaimed by the garbage collector. Objects are stored in some type of list or array called the pool, and handed out to the client on request. This is especially useful when an instance of an object is repeatedly used, or if the object has an expensive initialization aspect to its construction such that it's better to reuse an existing instance than to dispose of an existing one and to create a completely new one from scratch.

Let's consider a scenario in which object pooling would be useful. Suppose you are writing a system to archive patient information for a large insurance firm. Doctors collect information during the day and transmit it to a central location each evening. The code might contain a loop that does something like this:

while (IsRecordAvailable())
{
    PatientRecord record = GetNextRecord();
    ... // process record
}

In this loop, a new PatientRecord is returned each time the loop is executed. The most obvious implementation of the GetNextRecord method would create a new object each time it is called, requiring the object to be allocated, initialized, eventually garbage collected, and finalized if the object has a finalizer. When using an object pool, the allocation, initialization, collection, and finalization only occur once, reducing both the memory usage and the processing time that is required.

In some situations, the code could be rewritten to take advantage of a Clear method on the type with code like this:

PatientRecord record = new PatientRecord();
while (IsRecordAvailable())
{
    record.Clear();
    FillNextRecord(record);
    ... // process record
}

In this excerpt, a single PatientRecord object is created and a Clear method resets the contents so it can be reused within the loop. The FillNextRecord method uses an existing object, avoiding the repeated allocations. Of course, you still pay for a single allocation, initialization, and collection each time this code snippet is executed (though that's still better than paying for it every time through the loop). If the initialization is expensive, or the code is called simultaneously from multiple threads, the impact of this repeated creation may still be an issue.

The basic pattern for object pooling looks something like this:

while (IsRecordAvailable())
{
    PatientRecord record = Pool.GetObject();

    record.Clear();
    FillNextRecord(record);
    ... // process record

    Pool.ReleaseObject(record);
}

A PatientRecord instance, or pool of instances, is created at the start of the application. This code retrieves an instance from the pool, avoiding the memory allocation, construction, and eventual garbage collection. This produces a great savings of time and memory, although it requires the programmer to explicitly manage objects in the pool.

The .NET Framework provides object pooling for COM+ assemblies as part of its Enterprise Services support. Access to this functionality is provided via the System.EnterpriseServices.ObjectPoolingAttribute class. Rocky Lhotka has a good article on this feature at Everyone Into the Pool. COM+ provides pooling support automatically, so you don't have to remember to retrieve and return the objects explicitly. On the other hand, your assembly must operate within COM+.

For pooling any .NET object, I thought it would be interesting to write a general-purpose object pool for this article. My interface for this class is shown in Figure 4. The ObjectPool class provides pooling for any .NET type.

Figure 4 ObjectPool in .NET (Close Figure 4)
public class ObjectPool
{
    // ObjectPool is implemented as a Singleton
    public static ObjectPool GetInstance() { ... }

    // Delegates used by the interface
    public delegate object CreateObject();
    public delegate void UseObject(object obj, object [] args);

    // Initiate pooling for the given type
    public void RegisterType(Type t, CreateObject createDelegate,
        short minPoolSize, short maxPoolSize, int creationTimeout) { ... }

    // Terminate pooling for the given type
    public void UnregisterType(Type t) { ... }

    // Get and release objects in the pool
    public object GetObject(Type t) { ... }
    public void ReleaseObject(object obj) { ... }

    // Execute the given method using an object from the pool
    public void ExecuteFromPool(Type t,
        UseObject executeDelegate, object [] args) { ... }
}

Before a type can be pooled, it must first be registered. Registration identifies a creation delegate to call when a new instance of the object is required. This delegate simply returns the newly instantiated object and leaves the construction logic up to the client supplying the delegate. Like the Enterprise Services ObjectPooling attribute, it also accepts the minimum number of objects to keep active in the pool, the maximum number of objects to allow, and a timeout value for how long to wait for an available object. If the timeout is zero, then a caller will always wait until a free object is available. A nonzero timeout is useful in real-time or other situations where an alternate action may be required if an object is not readily available. After the registration call returns, the requested minimum number of objects is available in the pool. Pooling of a given type can be terminated with the UnregisterType method.

After registration, the GetObject and ReleaseObject methods retrieve and return objects from and to the pool. The ExecuteFromPool method accepts a delegate and arguments in addition to the desired type. The execute method invokes the given delegate with an object from the pool, and ensures that the retrieved object is returned to the pool after the delegate completes. This adds the overhead of a delegate invocation, but relieves you from having to manage the pool manually.

Internally, the class maintains a hash table of all pooled objects. It defines an ObjectData class to hold internal data related to each type. This class is not shown here, but maintains the registration information and records usage information for the type and maintains a queue of pooled objects.

The ReleaseObject method internally uses a private ReturnToPool method to restock the pool with the given object, shown in Figure 5. The Monitor class locks the operation. If fewer than the minimum number of objects is available, then the reference to the object is placed in the queue. If the minimum number of objects is already allocated, then a weak reference to the object is enqueued. If necessary, a waiting thread is signaled to pick up the newly enqueued object.

Figure 5 ReleaseObject Method (Close Figure 5)
private void ReturnToPool(object obj, ObjectData data)
{
    Monitor.Enter(data);
    try
    {
        data.inUse—;

        int size = data.inUse + data.inPool;
        if (size < data.minPoolSize)
        {
            // Return actual object to the pool
            data.pool.Enqueue(obj);
            data.inPool++;
        }
        else
        {
            // Min available, so enqueue weak reference
            WeakReference weakRef = new WeakReference(obj);
            data.pool.Enqueue(weakRef);
        }

        // Notify waiting threads
        if (data.inWait > 0) Monitor.Pulse(data);
    }
    finally { Monitor.Exit(data); }
}

Using a weak reference here keeps objects that are above the minimum around as long as possible, but makes them available for GC as required. The inUse field of ObjectData tracks objects given to the app, while the inPool field tracks how many actual references are in the pool. The inPool field ignores any weak references.

One of the most important things to implement when authoring a pool is a suitable object lifetime policy. Weak references form the basis of one such policy, but there are others, and which policy to use is based on circumstance.

For the GetObject method, the internal RetrieveFromPool method is shown in Figure 6. The Monitor.TryEnter method is used to ensure the application does not wait too long for the lock. If the lock cannot be obtained in the timeout period, null is returned to the caller. If the lock is held, the DequeueFromPool method is called to retrieve an object from the pool. Notice how this method accounts for possible weak references in the queue with the do-while loop.

Figure 6 RetrieveFromPool Method (Close Figure 6)
private object AllocateObject(ObjectData data)
{
    return data.createDelegate();
}

private object DequeueFromPool(ObjectData data)
{
    object result;
    do
    {
        // This presumes pool is non-empty
        result = data.pool.Dequeue();
        if (result is WeakReference)
            result = ((WeakReference)result).Target;
        else
            data.inPool—;

    } while (result == null && data.pool.Count > 0);

    return result;
}

private object RetrieveFromPool(ObjectData data)
{
    object result = null;
    int waitTime = (data.creationTimeout > 0) ?
        data.creationTimeout : Timeout.Infinite;

    try
    {
        // Try to obtain lock
        int startTick = Environment.TickCount;
        if (Monitor.TryEnter(data, waitTime) == false) return null;

        if (data.pool.Count > 0) result = DequeueFromPool(data);

        if (result == null)
        {
            // Pool empty or all weak refs
            if (data.maxPoolSize == 0 || data.inUse < data.maxPoolSize)
                result = AllocateObject(data);
            else
            {
                if (waitTime != Timeout.Infinite)
                    waitTime -= (Environment.TickCount - startTick);
                result = WaitForObject(data, waitTime);
            }
        }

       // Update inUse counter.
       if (result != null) data.inUse++;
  }
  finally { Monitor.Exit(data); }

  return result;
}

Back in the RetrieveFromPool code, if an entry is not found in the queue, a new object is allocated via the AllocateObject method, as long as fewer than the maximum number of objects is available. Once the maximum is reached, the WaitForObject method waits for an object until the creation timeout is reached. Note how the time to wait is adjusted before calling WaitForObject to account for time spent acquiring the lock. The WaitForObject code is not shown here, but is available in the download for this article.

There were two options for what should happen when the retrieval timeout occurs: return null or throw an exception. A disadvantage of returning null is that it forces a caller to check for null each time an object is obtained from the pool. Throwing an exception avoids this check, but makes the timeout more expensive. If timeouts are not expected, then throwing an exception might be a better choice. I decided to return null, because when timeouts are not expected, this check could be skipped. When timeouts are expected, the cost of checking for null is lower than the cost of catching an exception.

Figure 7 shows the code for the ExecuteFromPool method, with the error checking and comments removed. This code uses the private methods to retrieve an object from the pool and call the provided delegate. The finally block ensures that the object is returned to the pool even if an exception occurs.

Figure 7 ExecuteFromPool Method (Close Figure 7)
private ObjectData GetObjectData(Type t)
{
    // The private ObjectData class holds stats for each type.
    ObjectData data = Table[t.FullName] as ObjectData;
    if (data == null) throw new ArgumentException(...);
    return data;
}

public void ExecuteFromPool(Type t,
    UseObject executeDelegate, object [] args)
{
    ObjectData data = GetObjectData(t);

    object obj = null;
    try
    {
        // Retrieve an object from the pool
        obj = RetrieveFromPool(data);
        if (obj == null) throw new ObjectPoolException(...);

        // Execute given delegate with pooled object
        executeDelegate(obj, args);
    }
    finally
    {
        // Return retrieved object to pool
        if (obj != null) ReturnToPool(obj, data);
    }
}

Object pooling helps level out the number of allocations made on the heap, since the most common objects in an application can be pooled. This can get rid of the sawtooth pattern often seen for the managed heap size in .NET-based applications, and reduces the time an application spends performing garbage collection. I will look at a sample program that uses the ObjectPool class later.

Note that the managed heap is very efficient at allocating new objects, and the garbage collector is very efficient at collecting lots of small and short-lived objects. If your objects are not used with high frequency or do not have a high creation or destruction cost, then object pooling might not be the right strategy. As with any performance decision, profiling an application is the best way to get a handle on the real bottlenecks in the code.

Data Streaming

When managing large chunks of data, sometimes an application simply needs a lot of memory. Object pooling only helps reduce the memory required for class allocations and the time required for object creation and destruction. It does not really address the fact that some programs must handle a lot of data to perform their work.

When a large amount of data is required on an ongoing basis, the most you can do is manage memory as best you can, or perhaps compress or otherwise keep it as compact as possible. (Again, the classic trade-off between memory and speed arises, as compression reduces memory consumption but that compression requires cycles.) When data is required temporarily, you might be able to reduce the amount of memory utilized using data streaming. Data streaming is achieved by working on part of the data at a time, rather than all or most of the data all at once. Compare the DataSet and DataReader classes in the System.Data namespace. While you can load the results of a query directly into a DataSet object, a large query result will consume a large amount of memory. A DataSet also requires that the memory is accessed twice: once to fill the tables, and then later to read the tables. The DataReader class can incrementally load the results of the same query and present a single row at a time to an application. This is ideal when the entire resultset is not actually required, since it more effectively uses the available memory.

The String class provides a number of opportunities to consume large amounts of memory unintentionally. The simplest example is the concatenation of strings. Concatenating four strings incrementally (adding one string at a time to the new string) will internally produce seven string objects, since each addition produces a new string. The StringBuilder class in the System.Text namespace joins strings together without allocating a new string instance each time; this efficiency greatly improves memory utilization. The C# compiler also helps in this regard because it transforms a series of string concatenations in the same code statement into a single call to String.Concat.

The String.Replace method provides another example. Consider a system that reads and processes a number of input files sent from an external source. These files might require preprocessing to put them into an appropriate format. For discussion purposes, suppose I had a system that had to replace each occurrence of the word "nation" with "country", and each occurrence of the word "liberty" with "freedom". This can be done quite easily with the following code snippet:

using(StreamReader sr = new StreamReader(inPath))
{
    string contents = sr.ReadToEnd();
    string result = contents.Replace("nation", "country");
    result = result.Replace("liberty", "freedom");

    using(StreamWriter sw = new StreamWriter(outPath))
    {
        sw.Write(result)
    }
}

This works perfectly, at the expense of creating three strings that are the length of the file. The Gettysburg Address is roughly 2400 bytes of Unicode text. The U.S. Constitution is over 50,000 bytes of Unicode text. You see where this is going.

Now suppose each file was roughly 1MB of string data and I had to process up to 10 files concurrently. Reading and processing these 10 files will consume, in our simple example, around 10MB of string data. This is a rather large amount of memory for the garbage collector to allocate and clean up on an ongoing basis.

Streaming the file allows us to look at a small portion of the data at a time. Whenever I find an N or an L, I look for the words and replace them as required. Sample code is shown in Figure 8. I use the FileStream class in this code to illustrate operating on data at the byte level. You can modify this to use the StreamReader and StreamWriter classes instead if you want.

Figure 8 File Streaming (Close Figure 8)
static void ProcessFile(FileStream fin, FileStream fout)
{
    int next;
    while ((next = fin.ReadByte()) != -1)
    {
        byte b = (byte)next;
        if (b == 'n' || b == 'N')
          CheckForWordAndWrite(fin, fout, "nation", "country", b);
        else if (b == 'l' || b == 'L')
          CheckForWordAndWrite(fin, fout, "liberty", "freedom", b);
        else
          fout.WriteByte(b);
    }
}

static void CheckForWordAndWrite(Stream si, Stream so,
    string word, string replace, byte first)
{
    int len = word.Length;

    long pos = si.Position;
    byte[] buf = new byte[len];
    buf[0] = first;
    si.Read(buf, 1, word.Length-1);

    string data = Encoding.ASCII.GetString(buf);
    if (String.Compare(word, data, true) == 0)
        so.Write(Encoding.ASCII.GetBytes(replace), 0, replace.Length);
    else
    {
        si.Position = pos;    // reset stream
        so.WriteByte(first);  // write orig byte
    }
}

In the code, the ProcessFile method receives the two streams and reads one byte at a time, looking for an N or L. When one is found, the CheckForWordAndWrite method examines the stream to see if subsequent characters match the desired word. If a match is found, the replacement is written to the output stream. Otherwise, the original character is placed in the output stream, and the input stream is reset to the original position.

This method relies on the FileStream class to buffer the input and output files appropriately, so that the code can work byte by byte to perform the necessary processing. By default, each FileStream uses a buffer of 8KB, so this implementation uses much less memory than the prior code that reads and processes the entire file. Even so, this process makes a function call to FileStream.ReadByte and one to FileStream.WriteByte for most characters in the input stream. You might be able to find a happier medium by reading a series of bytes into a buffer at a time, thus saving the method calls. Again, a profiler is your friend.

The streaming classes in .NET are constructed to allow multiple streams to work together on a common base stream. Many classes derived from Stream include a constructor that takes an existing Stream object, allowing a chain of Stream objects to operate on the incoming data and produce a succession of modifications or transformations to the stream. For an example, see the .NET Framework documentation for the CryptoStream class, which shows you how to encrypt a byte array from an incoming FileStream object.

Now that I have examined some design and implementation issues related to memory utilization, a brief discussion on testing and tuning an application is in order. Almost any application is bound to have various performance and memory issues. The best way to discover them is to measure these items explicitly and track down problems as they are revealed. Windows® performance counters and the .NET CLR Profiler or other profilers are two great ways to achieve that end.

Performance Monitoring

The Windows Performance Monitor does not solve performance problems, but it does help identify where you should look for them. There is an exhaustive list of performance counters related to memory utilization and other performance metrics available at Chapter 15 — Measuring .NET Application Performance.

Performance tuning is ideally an iterative task. Once a set of performance metrics are identified and a test environment is established where these metrics can be applied, the application is run within the test environment. Performance information is gathered using Performance Monitor. The results are analyzed to produce some suggested areas for improvement. The application or configuration is modified based on these suggestions, and the process begins again.

This process of testing, gathering, analyzing, and modifying a system applies equally well to all aspects of performance, including memory utilization. The modification of a system may include rewriting a portion of the code, changing the configuration or distribution of apps within the system, or other changes.

CLR Profiler

The CLR Profiler tool is great for memory utilization analysis. It profiles the behavior of an application as it is running and provides detailed reports on the types allocated, how long they are allocated, the details of each garbage collection, and additional memory-related information. You can download this free tool from Tools & Utilities .

The profiling tool is quite intrusive, so is not suitable for general performance analysis. For analyzing the managed heap, however, it is very impressive. To see a small sample of its capabilities, I wrote a small PoolingDemo program that uses the ObjectPool class I discussed earlier. Lest you think pooling is only for big or expensive objects, this demo defines a MyClass object as follows:

class MyClass {
    Random r = new Random();

    public void DoWork() {
        int x = r.Next(0, 100);
    }
}
The program allows you to choose between a non-pooling and a pooling test. The non-pooling code does the following:
public static void BasicNoPooling()
{
    for (int i = 0; i < Iterations; i++)
    {
        MyClass c = new MyClass();
        c.DoWork();
    }
}

On my desktop machine, one million iterations takes about 12 seconds to complete. The pooling code avoids the allocation of the MyClass object within the loop:

public static void BasicPooling()
{
    // Register the MyClass type
    Pool.RegisterType(typeof(MyClass), ...);

    for (int i = 0; i < Iterations; i++)
    {
        MyClass c = (MyClass)Pool.GetObject(typeof(MyClass));
        c.DoWork();

        Pool.ReleaseObject(c);
    }

    Pool.UnregisterType(typeof(MyClass));
}

In this code, I use a static Pool property to invoke ObjectPool.GetInstance. At one million iterations, this pooling test takes roughly 1.2 seconds to complete, approximately 10 times faster than the non-pooling code. Of course, my example is contrived to emphasize the costs associated with obtaining and releasing references to instances of objects. The MyClass.DoWork is almost certainly inlined by the JIT compiler, and the per-iteration savings (10 seconds over one million iterations) is quite small. Still, this example illustrates how object pooling can eliminate a certain amount of overhead. In situations where such overhead is important or the time to create or finalize an object is expensive, object pooling may prove beneficial.

Figure 9 Time Line View With Object Pooling
Figure 9 Time Line View With Object Pooling

Reducing the iterations to 100,000 and running the CLR Profiler over this code produces some interesting results. Figure 9 shows the Time Line View when using object pooling and Figure 10 shows it when no pooling is used. This view shows a time line of the managed heap with different types represented by different colors, and includes the timing of each garbage collection. In Figure 9, pooling produces a fairly level heap, with a single garbage collection as the application exits. In Figure 10, without pooling, the heap must recover the data allocated by each Random class. The red represents integer arrays, which is the bulk of the data. The Time Line View without object pooling shows that 11 garbage collections were performed by the non-pooling test.

Figure 10 Time Line View Without Object Pooling
Figure 10 Time Line View Without Object Pooling

CLR Profiler can also show allocation views by class type or over time, identify the number of bytes allocated by each method, and display the sequence of methods executed over the life of the test. The CLR Profiler download, available on the MSDN®Magazine Web site, contains some fairly extensive documentation, including a section with sample code showing common garbage collection problems and how they show up in the various CLR Profiler views.

Conclusion

I'm sure by now you're thinking differently about memory utilization in your code—where it is good and where it could be better. I've covered a range of issues here, from the sizing of types to tools that can help you discover memory issues in your code. I discussed the performance and memory benefits of pooling frequently used objects rather than relying on the .NET runtime to allocate and then garbage collect the objects, and I looked at streaming as a way to reduce the amount of memory required for processing large objects. The rest is up to you.

Erik Brown is a senior developer and architect at Unisys Corporation, a Microsoft Gold Certified Partner. He is author of the book Windows Forms Programming with C# (Manning Publications Company, 2002).


CLR Profiler: No Code Can Hide from the Profiling API in the .NET Framework 2.0

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

CLR Profiler

No Code Can Hide from the Profiling API in the .NET Framework 2.0

Jay Hilyard
This article is based on a prerelease version of the .NET Framework 2.0, formerly code-named "Whidbey." All information contained herein is subject to change.
This article discusses:
  • Looking into the .NET runtime
  • More detailed function tracing
  • Better thread and call stack information
  • The phase-out of in-process debugging
This article uses the following technologies:
The .NET Framework
Code download available at: CLRProfiler.exe (307KB)
Contents
So What's New?
What's Gone?
Obtaining Thread Information
Stack 'Em Up
Enhanced Tracing
Describing and Decoding Function Arguments
Conclusion

S
ince the inception of .NET, the common language runtime (CLR) profiling API has been the mechanism to use to inspect what the runtime is doing under the covers. Many profilers simply report how much time is spent in a given routine, file, or class, but the profiling API is much more complete in the amount and types of data it makes available. Information about the application domains, assemblies, and classes that are loaded and used in a process, just-in-time (JIT) compiler notifications, memory usage tracking, tracing of events, exception tracking, managed to unmanaged code transitions, and the state of the runtime are all categories of information available to a profiler written for the .NET Framework 1.0 and 1.1 using the profiling API.

You will find a nicely enhanced profiling API in the .NET Framework 2.0 Beta 1. In this article, I will discuss why the changes were made and will describe how developers can take advantage of them.

If you have not worked with the profiling API before, let me explain its capabilities before I start discussing the new enhancements. First and foremost, using the profiling API requires an unmanaged COM server DLL that implements the ICorProfilerCallback interface. In addition, you need to set up two environment variables (COR_PROFILER and COR_ENABLE_PROFILING) to let the CLR know that it should load your DLL as a profiler. COR_PROFILER is set to the class ID (CLSID) of your COM server and COR_ENABLE_PROFILING is set to 1.

Once the CLR has created an instance of your profiler, the first notification you will receive on the ICorProfilerCallback interface is the Initialize notification. This gives you the opportunity to tell the CLR what types of notifications you would like to receive by calling ICorProfilerInfo::SetEventMask with a combination of flags that are defined by the COR_PRF_MONITOR enumeration. ICorProfilerInfo allows you to ask the CLR about the currently running program and act accordingly. The flags in COR_PRF_MONITOR are enabled at the very beginning of the program, though they can be turned on and off by calling SetEventMask again later in the program execution with any new flags you want.

Another thing you can do in the original profiling API is set up callbacks using the ICorProfilerInfo::SetEnterLeaveFunctionHooks call. This allows you to provide callback functions to the CLR that will be called every time a function is entered or exited, or when a tailcall occurs (these do not return). When the program terminates, you receive a shutdown notification from the CLR, at which point you have a chance to perform final cleanup of any resources in use. The original profiling API allowed many different ways of exploring your program as it ran, but as with all things, there was room for improvement.

For more background on the profiling API, see my overview of it in the January 2003 issue of MSDN®Magazine, available at Inspect and Optimize Your Program's Memory Usage with the .NET Profiler API, as well as Aleksandr Mikunov's article on MSIL code rewriting using the profiling API, available in the September 2003 issue of MSDN Magazine at Rewrite MSIL Code on the Fly with the .NET Framework Profiling API .

So What's New?

The profiling team at Microsoft listened to developers and added a number of cool capabilities in 2.0. New things you can do with the profiling API include tracking the name and application domain of a managed thread, obtaining function arguments and return values without using in-process debugging, inspecting these arguments for values, and getting a stack trace quickly.

Much of this functionality is exposed through two new interfaces: ICorProfilerCallback2 and ICorProfilerInfo2. The ICorProfilerCallback2 interface inherits from the original ICorProfilerCallback interface and adds a new method for tracking thread name changes as well as eight new methods for garbage collection notifications. This is discussed in the section on thread information a bit later in this article. The ICorProfilerInfo2 interface inherits from ICorProfilerInfo and adds 21 new methods for controlling interactions with the runtime.

The ICorProfilerInfo::SetEventMask function is called to tell the runtime for which events we are interested in receiving callbacks. The event mask is comprised of the COR_PRF_MONITOR enumeration to which five new flags have been added for the .NET Framework 2.0, as shown in Figure 1. [ Editor's Update - 1/9/2006: The tables in the figures have been updated to reflect changes made for the release of the .NET Framework 2.0.]

Figure 1 New COR_PRF_MONITOR Flags (Close Figure 1)

Flag | Value
COR_PRF_ENABLE_FUNCTION_ARGS | 0x02000000
COR_PRF_ENABLE_FUNCTION_RETVAL | 0x04000000
COR_PRF_ENABLE_FRAME_INFO | 0x08000000
COR_PRF_ENABLE_STACK_SNAPSHOT | 0x10000000
COR_PRF_USE_PROFILE_IMAGES | 0x20000000

However, all of these flags have also been added to the COR_PRF_IMMUTABLE enumeration value, which means that they can be set only with the ICorProfilerCallback::Initialize method. Any attempt to reset them at a later point by calling ICorProilerInfo::SetEventMask again will fail. COR_PRF_IMMUTABLE consists of the following COR_PRF_ MONITOR flags ORed together, as shown in Figure 2.

Figure 2 COR_PRF_IMMUTABLE Flags (Close Figure 2)
COR_PRF_MONITOR_CODE_TRANSITIONS
COR_PRF_MONITOR_REMOTING
COR_PRF_MONITOR_REMOTING_COOKIE
COR_PRF_MONITOR_REMOTING_ASYNC
COR_PRF_MONITOR_GC
COR_PRF_ENABLE_REJIT
COR_PRF_ENABLE_INPROC_DEBUGGING
COR_PRF_ENABLE_JIT_MAPS
COR_PRF_DISABLE_OPTIMIZATIONS
COR_PRF_DISABLE_INLINING
COR_PRF_ENABLE_OBJECT_ALLOCATED
COR_PRF_ENABLE_FUNCTION_ARGS
COR_PRF_ENABLE_FUNCTION_RETVAL
COR_PRF_ENABLE_FRAME_INFO
COR_PRF_ENABLE_STACK_SNAPSHOT
COR_PRF_USE_PROFILE_IMAGES

COR_PRF_ENABLE_FUNCTION_ARGS turns on two specific capabilities. The first is the ability to get function arguments in callbacks to FunctionEnter2 and FunctionTailcall2 (both new for the .NET Framework 2.0). The second is the ability to use the ICorProfilerInfo2::GetFunctionInfo2 function to get exact Class IDs for generic functions. If this flag is not used and you register for the FunctionEnter2 callback, the COR_PRF_FUNCTION_ARGUMENT_INFO* and COR_PRF_FRAME_INFO parameters to these functions will always be NULL. COR_PRF_ ENABLE_FUNCTION_RETVAL turns on the ability to track return values through the FunctionLeave2 callback via the COR_PRF_FUNCTION_ARGUMENT_RANGE* parameter. This parameter will always be NULL if this flag is not set in ICorProfilerInfo::SetEventMask.

Let's quickly touch on what is no longer available from the original profiling API. Then, we will explore how to use the powerful new functionality of Visual Studio®.

What's Gone?

The main feature being phased out with this release of the profiling API is the concept of in-process debugging. This was the original method used by profiler writers to examine values of function calls and return values as well as program state at run time. This was accomplished through four ICorProfilerInfo API methods (shown in Figure 3), and an interface called ICorDebug. ICorDebug is no longer used in conjunction with the profiling API.

Figure 3 Retired Profiling APIs (Close Figure 3)

ICorProfilerInfo Interface Method | Description
BeginInprocDebugging | Indicates that a profiler wants to start using in-process debugging
GetInprocInspectionInterface | Used to get an ICorDebug interface for full stack traces and other process information
GetInprocInspectionThisThread | Used to get an ICorThread interface for thread-specific stack information
EndInprocDebugging | Indicates that a profiler wants to stop using in-proc debugging

One of the downfalls of in-process debugging was its inability to correctly and reliably deliver method parameter and return values during FunctionEnter, FunctionLeave, and FunctionTailcall callbacks. This was due to the relatively complex nature of the interactions between ICorDebug and the JIT compiler in-process. Happily for us, the Microsoft profiling API team has replaced this with a better-performing version of the same functionality originally envisioned for ICorDebug.

Obtaining Thread Information

The flag previously needed to get threading information in a profiler was COR_PRF_MONITOR_THREADS, a value set during the call to SetEventMask. In the new version of the Profiling API, you can use the same flag, but you will get more information if you have implemented ICorProfilerCallback2. ThreadNameChanged is a new method on ICorProfilerCallback2:

HRESULT ICorProfilerCallback2::ThreadNameChanged(
[in] ThreadID threadId, // ID of thread whose name was changed
[in] ULONG cchName,  // number of characters in new name
[in] WCHAR name[]) // the new name of the thread

ThreadNameChanged is called when the friendly name is set on a .NET thread by assigning System.Threading.Thread.Name to a new value. Initially, .NET threads are not named, but if you are doing any sort of serious multithreading, I suggest naming them; this can prove an invaluable aid when debugging thread issues. It is much easier to manage threads named "GUI" and "Worker" than "0x98745837" and "0x22345873".

Another newly exposed piece of thread information is the AppDomain in which a thread is currently executing. This is retrieved with a call to ICorProfilerInfo2::GetThreadAppDomain:

HRESULT ICorProfilerInfo2::GetThreadAppDomain (
[in] ThreadID threadId, // ID of the thread whose AppDomain you require
[out] AppDomainID *pAppDomainId) // AppDomainID for the requested thread

GetThreadAppDomain will allow you to get the ID for the current AppDomain associated with a given runtime thread. This may be able to help you solve bugs where threads are attempting to access AppDomain-specific resources when the thread is no longer in that AppDomain.

Stack 'Em Up

Another new profiling API capability is the ability to request a stack snapshot. This allows the profiler to create a stack trace by getting callbacks for each managed frame, and potentially the starting point for any unmanaged frames as well (ideal for use with an unmanaged stack walker). [ Editor's Update - 1/9/2006: In order to use stack snapshots, ICorProfilerInfo2::SetEventMask must include the new COR_PRF_ENABLE_STATIC_SNAPSHOT flag.] To start the snapshot of the current thread's stack, call ICorProfilerInfo2::DoStackSnapshot, like so: [ Editor's Update - 1/9/2006: Many of the ICorProfilerInfo2 methods described in this article, including DoStackSnapshot, were updated for the final release of the .NET Framework 2.0. Please see the documentation at ICorProfilerInfo2 for updated signatures and information.]

HRESULT ICorProfilerInfo2::DoStackSnapshot (
[in] ThreadID thread,   // thread for which snapshot should be taken
[in] StackSnapshotCallback *callback, // callback for each frame
[in] ULONG32 infoFlags, // COR_PRF_SNAPSHOT_INFO flags to control
                        // the degree of information for each snapshot
clientData) // caller-specified data passed to each callback

DoStackSnapshot will perform a stack snapshot of the current thread. During the snapshot, the callback function whose signature is defined by StackSnapshotCallback is called once per frame of the managed stack and once at the beginning of an unmanaged chain. A profiler can do unmanaged stack walking from this point if it is tracking unmanaged code in addition to managed code, or it can ignore the unmanaged stack markers and just trace the managed frames of the stack. The definition of the StackSnapshotCallback function looks like this:

HRESULT StackSnapshotCallback(
[in] FunctionID funcId, // function ID for reported frame; 0 for unmanaged
[in] UINT_PTR ip, // instruction pointer for next instruction in frame
[in] COR_PRF_FRAME_INFO frameInfo, // opaque value used to get frame info
[in] ULONG32 contextSize, // size of buffer in context parameter
[in] BYTE [] context, // register context of the frame; can be NULL
[in] void* clientData),// data passed via DoStackSnapshot call

StackSnapshotCallback allows the recipient to examine the frame of the stack that the snapshot is currently on. This includes getting info available via the COR_PRF_FRAME_INFO value, seeing the register information, and reading any supporting data passed in from the original caller of DoStackSnapshot in the clientData field.

The opaque value COR_PRF_FRAME_INFO is defined as shown in the following line of code:

typedef UINT_PTR COR_PRF_FRAME_INFO
COR_PRF_FRAME_INFO is used to represent a stack frame and act as a token for methods on ICorProfilerInfo2. This allows ICorProfilerInfo2 to retrieve parameter and return value information.

The COR_PRF_SNAPSHOT_INFO enumeration contains the values to pass as the infoFlags parameter value in DoStackSnapshot. These values are listed in Figure 4.

Figure 4 COR_PRF_SNAPSHOT_INFO Enumeration Values (Close Figure 4)

Enumeration Value | Description
COR_PRF_SNAPSHOT_DEFAULT | This will return only the default set of stack information that is comprised of the FunctionID for the frame method, the IP (instruction pointer), and the COR_PRF_FRAME_INFO
COR_PRF_SNAPSHOT_ | This will allow the unmanaged
REGISTER_CONTEXT | register contexts for unmanaged frames to be passed in the context field of the StackSnapshotCallback

Using DoStackSnapshot every time you need a stack is fine as long as you don't need stacks too often. It also has the added bonus of making it relatively easy to interleave unmanaged stack frames; if you need full stacks of both managed and unmanaged code ranges, you must use DoStackSnapshot because it provides the unmanaged frame starting points. If you are only interested in managed stack walking, then you can use the enhanced tracing capabilities of the new FunctionEnter2, FunctionLeave2, and FunctionTailcall2 tracing functions. These three functions allow you to monitor the progression of your code and build a "shadow stack." If you often need stack traces, building a shadow stack is less expensive and, as an added benefit, it provides you with generic type parameter information that the DoStackSnapshot method will never give to a profiler.

Enhanced Tracing

Function tracing is a core part of any profiler, as it allows you to see the entry and exit for each function in the application. In the 1.0 and 1.1 versions of the Profiling API, this was accomplished by setting callback functions on the ICorProfilerInfo interface through the SetEnterLeaveFunctionHooks method. The method allowed a profiler to sign up for three different types of function-tracing callbacks: entering a function (FunctionEnter), leaving a function (FunctionLeave), and performing a tailcall (FunctionTailcall). A tailcall occurs when the last action of a method is a call to another method.

What's interesting here is that the stack never records the call to the first method, only the second. These callbacks would execute for every function when the type of call or return occurred in the CLR, so you could get a real picture of where your application was doing its work. Each callback was passed a function ID, so a profiler could identify which function was being entered or exited; this function ID can be resolved by using the metadata API that is also provided for unmanaged access.

This was adequate for profilers who simply cared whether a function was entered or exited. However, to find out which parameters the function was called with or what the return value was on exit, the profiler was supposed to use the in-process debugging interface. As I mentioned earlier in this article, in-process debugging was not optimal for this purpose as there was no way for a profiler to detect these values.

Now we come to .NET Framework 2.0, where the profiling API team has liberated us from the use of ICorDebug for in-process debugging. They did this by adding a new method to the ICorProfilerInfo2 interface called SetEnterLeaveFunctionHooks2 as shown in the following:

HRESULT
ICorProfilerInfo2::SetEnterLeaveFunctionHooks2 (
// ptr to FunctionEnter2 callback
[in] FunctionEnter2    *pFuncEnter,
// ptr to FunctionLeave2 callback
[in] FunctionLeave2    *pFuncLeave,
// ptr to FunctionTailcall2 callback
[in] FunctionTailcall2 *pFuncTailcall)

To make the callback occur, the COR_PRF_MONITOR flag COR_PRF_MONITOR_ENTERLEAVE must be set using ICorProfilerInfo2::SetEventMask. To get the new functionality of being able to inspect the arguments of a function, the profiler must set the new flag, COR_PRF_ENABLE_FUNCTION_ARGS. And to get the return values, COR_PRF_ENABLE_FUNCTION_ RETVAL must be set as well.

If you have done work with the profiling API in the past, you will have noticed that the calling convention for the original FunctionEnter, FunctionLeave, and FunctionTailcall are _declspec(naked). This means that the prologue and epilogue for the function are not set up for the profiler by the compiler. The profiler needed to do a bit of inline assembly language to set up the prologue at the beginning of the function and the epilogue at the end of the function. The new callbacks for FunctionEnter2, FunctionLeave2, and FunctionTailcall2 use the _stdcall calling convention where none of this is necessary.

[ Editor's Update - 3/25/2006: This is incorrect. For the .NET Framework 2.0, the new callbacks for FunctionEnter2, FunctionLeave2, and FunctionTailcall2 should still in general be implemented as __declspec(naked).]

To register for the new function tracing callbacks, the profiling API provides the callback implementations for FunctionEnter2, FunctionLeave2, and FunctionTailcall2, as shown here:

typedef void FunctionEnter2 (
[in] FunctionID funcId, // FunctionID for the function being entered
[in] COR_PRF_FRAME_INFO func, // opaque value only valid during callback
[in] COR_PRF_FUNCTION_ARGUMENT_INFO *argumentInfo) // function args info
COR_PRF_ENABLE_FUNCTION_ARGS is not set in the event mask and will always be NULL.

FunctionEnter2 is called at the entering of almost every method (except tailcalls) during the execution of the program. This occurs after SetEnterLeaveFunctionHooks2 is called with the following callback pointer set:

typedef void FunctionLeave2 (
[in] FunctionID funcId, // FunctionID for function being left
[in] COR_PRF_FRAME_INFO func,  // opaque value only valid during callback
[in] COR_PRF_FUNCTION_ARGUMENT_RANGE *retvalRange) // info  about return
                                                   // values from function
COR_PRF_ENABLE_FUNCTION_RETVAL is not set in the event mask and will always be NULL.

FunctionLeave2 is called for the exiting of every method during the execution of the app. Again, this occurs after SetEnterLeaveFunctionHooks2 is called with this callback pointer set:

typedef void FunctionTailcall2 (
[in] FunctionID funcId, // FunctionID for the function
[in] COR_PRF_FRAME_INFO func) // opaque value only valid during callback

Describing and Decoding Function Arguments

Now that you have callbacks for the function at the point it is being entered and exited, you can examine arguments and return values. The Profiling API exposes these values as regions in memory. You can use a function's metadata token to get the types of the parameters you are dealing with, which will allow you to decode them. The .NET Framework keeps track of items by tagging them with values from the CorElementType enumeration. Figure 5 lists all of these values (entries in red are new enumeration values added in the .NET Framework 2.0). CorElementTypes with a value above 0x22 (ELEMENT_TYPE_ MAX) are special modifiers used to describe types in different circumstances. In the arguments and return values, you will see the basic .NET types, represented by the CorElementType enumeration. These are shown in Figure 6.

Figure 6 Argument and Return Value Common Types (Close Figure 6)

ELEMENT_TYPE | Representation
ELEMENT_TYPE <= R8, I, U | Primitive values
ELEMENT_TYPE_VALUETYPE | Use the ICorProfilerInfo2::GetClassLayout method to resolve layout
ELEMENT_TYPE_ (CLASS, STRING, OBJECT, ARRAY, GENERICINST, SZARRAY) | Object ID (pointer into garbage collector heap)
ELEMENT_TYPE_BYREF | Managed pointer (to the stack or garbage collector heap)
ELEMENT_TYPE_PTR | Unmanaged pointer (not movable by the garbage collector)
ELEMENT_TYPE_FNPTR | Pointer-sized opaque value
ELEMENT_TYPE_TYPEDBYREF | Managed pointer, followed by a pointer-sized opaque value
Figure 5 CorElementType Enumeration, Values, and Description (Close Figure 5)

Enumeration Value | Value (Hex) | Description
ELEMENT_TYPE_END | 0x0 | Undefined constant
ELEMENT_TYPE_VOID | 0x1 | Void return type
ELEMENT_TYPE_BOOLEAN | 0x2 | Boolean value
ELEMENT_TYPE_CHAR | 0x3 | WCHAR
ELEMENT_TYPE_I1 | 0x4 | Short
ELEMENT_TYPE_U1 | 0x5 | Unsigned short
ELEMENT_TYPE_I2 | 0x6 | Int
ELEMENT_TYPE_U2 | 0x7 | Unsigned int
ELEMENT_TYPE_I4 | 0x8 | Long
ELEMENT_TYPE_U4 | 0x9 | Unsigned long
ELEMENT_TYPE_I8 | 0xA | __int64
ELEMENT_TYPE_U8 | 0xB | Unsigned __int64
ELEMENT_TYPE_R4 | 0xC | Float
ELEMENT_TYPE_R8 | 0xD | Double
ELEMENT_TYPE_STRING | 0xE | String object
ELEMENT_TYPE_PTR | 0xF | Unmanaged pointer
ELEMENT_TYPE_BYREF | 0x10 | Managed pointer
ELEMENT_TYPE_VALUETYPE | 0x11 | Value type
ELEMENT_TYPE_CLASS | 0x12 | Specific class type
ELEMENT_TYPE_VAR | 0x13 | A generic class type variable (for example, MyClass <T> and <T> gets this CorElementType)
ELEMENT_TYPE_ARRAY | 0x14 | Multidimensional array
ELEMENT_TYPE_GENERICINST | 0x15 | Generic instantiated type
ELEMENT_TYPE_TYPEDBYREF | 0x16 | Managed pointer, followed by an ELEMENT_TYPE_FNPTR
ELEMENT_TYPE_I | 0x18 | Platform-independent int
ELEMENT_TYPE_U | 0x19 | Platform-independent unsigned int
ELEMENT_TYPE_FNPTR | 0x1B | Function pointer with the complete signature and calling convention
ELEMENT_TYPE_OBJECT | 0x1C | System.Object
ELEMENT_TYPE_SZARRAY | 0x1D | Single-dimensional array
ELEMENT_TYPE_MVAR | 0x1E | Generic method type variable (for example, void MyMethod<T> () and <T> gets this CorElementType)
ELEMENT_TYPE_CMOD_REQD | 0x1F | Binding flag for required C modifier
ELEMENT_TYPE_CMOD_OPT | 0x20 | Binding flag for optional C modifier
ELEMENT_TYPE_INTERNAL | 0x21 | Internally generated signature
ELEMENT_TYPE_MAX | 0x22 | Maximum of base types, all past this point are modifiers to the preceding base types
ELEMENT_TYPE_MODIFIER | 0x40 | Modifier flag
ELEMENT_TYPE_SENTINEL | 0x01 | 0x40 | Indicates the last argument in a varargs function
ELEMENT_TYPE_PINNED | 0x05 | 0x40 | This is pinned
ELEMENT_TYPE_R4_HFA | 0x06 | 0x40 | Internal CLR usage
ELEMENT_TYPE_R8_HFA | 0x07 | 0x40 | Internal CLR usage

The arguments for a FunctionEnter2 call or a FunctionTailcall2 call are passed in the COR_PRF_FUNCTION_ARGUMENT_ INFO* argumentInfo parameter. COR_PRF_FUNCTION_ ARGUMENT_INFO is a struct that's defined like this:

typedef struct _COR_PRF_FUNCTION_ARGUMENT_INFO {
    ULONG numRanges; // number of argument ranges
    ULONG totalArgumentSize; // total size of arguments
    COR_PRF_FUNCTION_ARGUMENT_RANGE ranges[ 1 ]; // array of ranges
}
COR_PRF_FUNCTION_ARGUMENT_INFO;

The ranges field is defined as a set of structures defined as COR_PRF_FUNCTION_ARGUMENT_RANGE and shown here:

typedef struct _COR_PRF_FUNCTION_ARGUMENT_RANGE {
    UINT_PTR startAddress; // starting memory address of first argument
    ULONG length; // length of this range of memory
}
COR_PRF_FUNCTION_ARGUMENT_RANGE;
If a profiler wants to reference any of this information later, it needs to copy it, since these structures will not be valid once you leave the FunctionEnter2 callback. COR_PRF_FUNCTION_ARGUMENT_INFO is essentially a map to the function argument values (for Value Types) or Object IDs (for Reference Types). If the arguments are contiguous in memory in left-to-right order, then one COR_PRF_FUNCTION_ARGUMENT_RANGE structure will be used to represent them. This range is pulled apart by looking at the function signature which can be retrieved from the metadata using the unmanaged metadata API, and then using that knowledge to walk along the memory range and get each argument's value or Object ID.

For a FunctionLeave2 call, only the return values are provided in a range specified by a COR_PRF_FUNCTION_ARGUMENT_RANGE structure. For items such as ref and out parameters, you need to save the memory address for the argument in the call to FunctionEnter2 and then check it in the Leave callback.

Now that you have the values and/or object IDs for the arguments and return values, what do you do with them? It's easy to print out a simple integer value, but what if the value is an object ID? Help is needed to decode what type of object you are dealing with in order to access the values.

A whole new set of functions was added to the ICorProfilerInfo2 interface to help you retrieve the information for these parameters and more. This set includes the following new functions added to the ICorProfilerInfo2 interface:

  • GetFunctionInfo2
  • GetStringLayout
  • GetClassLayout
  • GetClassIDInfo2
  • GetClassFromTokenAndTypeArgs
  • GetFunctionFromTokenAndTypeArgs
  • GetArrayObjectInfo
  • GetBoxClassLayout

Let's start by figuring out some things about the function you are being called back about. In the previous versions of the profiling API, there was a function called ICorProfilerInfo::GetFunctionInfo. GetFunctionInfo let you use the Function ID passed in to the callback to determine a function's class and module; it even got the function's metadata token. In the grand tradition of API versioning, Microsoft now gives us the fabulously exciting method name ICorProfilerInfo2::GetFunctionInfo2.

What new and wondrous things can now be accessed? Well let's take a look at the definition for GetFunctionInfo2:

HRESULT GetFunctionInfo2 (
  [in] COR_PRF_FRAME_INFO frameInfo, // frame information for callback
  [out] ClassID *pClassId,   // class ID for function; can be NULL
  [out] ModuleID *pModuleId, // module ID for function; can be NULL
  [out] mdToken *pToken,     // metadata token for function; can be NULL
  [in] ULONG32 cTypeArgs,    // number of elements in the type arg array
  [out] ULONG32 *pcTypeArgs, // number of elements needed to hold type
                             // arguments to the function; can be NULL
  [out] ClassID typeArgs[])  // caller-allocated buffer to accept the
                             // the function's type arguments; can be NULL
If you are trying to examine different parts of the function signature, GetFunctionInfo2 allows you to get the class the function is part of, the module it belongs to, and the metadata token. (The latter can be used to find all sorts of information with the metadata API, but that's a topic for another time.)

You now have the capability to look at the type parameters on a generic function. All of the arguments with TypeArgs in the name help to describe the classes of the type arguments for this instantiation of this function. For example, consider a generic function defined like this:

public class Printer {
    public void Print <T>(T item) {
       // do some printing generically
    }
}
You can find out the type of T when this function was instantiated at run time for this callback. That's not all, though. Due to the way the CLR stores different types of items that can be arguments (strings, arrays, boxed items, and so on), a profiler needs a few helper methods to pluck the appropriate value from the correct place in memory. This is because the layout in memory of this data is not guaranteed by the CLR, since it could change from platform to platform and processor architecture to processor architecture.

The ICorProfilerInfo2::GetStringLayout function is provided to analyze string information:

HRESULT GetStringLayout (
[out] ULONG *pBufferLengthOffset, // offset to DWORD containing buffer len
[out] ULONG *pStringLengthOffset, // offset to DWORD containing string len
[out] ULONG *pBufferOffset)       // offset to the raw string buffer

Since all strings in .NET are Unicode-based, the buffer for the string actually contains wide characters (WCHAR). This must be taken into account when you process the string after retrieving it.

Not all parameters are value types. If the profiler is to make sense of reference type parameters, it needs to walk the layout of a class or structure in memory to find the individual member values. To help in this value quest, find the ICorProfilerInfo2::GetClassLayout method, as shown here:

HRESULT GetClassLayout (
[in]  ClassID classID, // class ID for which the layout is requested
[in, out] COR_FIELD_OFFSET rFieldOffset[], // caller allocated layout
                                           // of fields in the class
[in] ULONG cFieldOffset,    // number of COR_FIELD_OFFSET elements in
                            // the caller allocated buffer in rFieldOffset
[out] ULONG *pcFieldOffset, // actual number of COR_FIELD_OFFSET elements
[out] ULONG *pulClassSize); // size in bytes of the class
There is one COR_FIELD_OFFSET structure for each field in the class, with COR_FIELD_OFFSET defined as:
typedef struct _COR_FIELD_OFFSET {
    mdFieldDef ridOfField; // fieldDef metadata token for this field
    ULONG      ulOffset;   // offset of field from beginning of object
}
COR_FIELD_OFFSET;

To get at the field values using the information returned from GetClassLayout, you need to add the ulOffset field's COR_FIELD_OFFSET value to the object ID of the field. You will then have the address in memory where this field's value resides. Note that fields that are reference types have values that can be used as object IDs for further inspection using GetClassLayout.

To get information about the parent module that a class belongs to, as well as the metadata token for the class, a profiler used to call ICorProfilerInfo::GetClassIDInfo. Now, to support generics properly, it calls ICorProfilerInfo2::GetClassIDInfo2:

HRESULT GetClassIDInfo2(
[in] ClassID classId,       // class ID for which information is required
[out] ModuleID *pModuleId,      // module to which this class belongs
[out] mdTypeDef *pTypeDefToken, // metadata token for the class
[in] ULONG32 cNumTypeArgs,      // number of ClassID slots in typeArgs
[out] ULONG32 *pcNumTypeArgs,   // actual number of type arguments
[out] ClassID typeArgs[])..     // type argument Class IDs for this class

GetClassIDInfo2 generalizes GetClassIDInfo for all types, generic and non-generic, as demonstrated by the ability to get the type parameters for a class. If you call GetClassIDInfo with zero specified for the cNumTypeArgs, but provide a pointer to a ULONG32 to return the correct number in, you can figure out how much space you need. Then, you can allocate the array of class IDs to pass in typeArgs and make the actual call with the correctly-sized array.

Now that you know how to get the metadata token and type arguments for a class from a class ID, it only seems fair to show you how to round-trip and get a class ID for a metadata token and some type arguments. This is just what the GetClassFromTokenAndTypeArgs is used for, as shown in this code:

HRESULT GetClassFromTokenAndTypeArgs(
[in] ModuleID moduleID,  // module to which the class belongs
[in] mdTypeDef typeDef,  // metadata token for the class
[in] ULONG32 cTypeArgs,  // number of ClassID slots allocated in typeArgs
[in, size_is(cTypeArgs)] ClassID typeArgs[], // argument Class IDs
[out] ClassID* pClassID) // pointer which receives the Class ID

To do the same thing for a function, let's turn again to the longer-named methods on the ICorProfilerInfo2 interface and find GetFunctionFromTokenAndTypeArgs:

HRESULT GetFunctionFromTokenAndTypeArgs(
[in] ModuleID moduleID, // module to which function belongs
[in] mdTypeDef funcDef, // metadata token for the function
[in] ClassID classId,   // class ID for the class containing the function
[in] ULONG32 cTypeArgs, // number of slots in typeArgs
[in, size_is(cTypeArgs)] ClassID typeArgs[], // argument Class IDs
[out] FunctionID* pFunctionID) // pointer which receives the Function ID
There you have it—a way to go back and forth with both class IDs and function IDs.

Figure 7 .NET Arrays
Figure 7 .NET Arrays

What else can the new profiling API do? Well, it helps a profiler discover how to access elements in various types of arrays. Three types of arrays are available in the .NET Framework: single-dimensional arrays, traditional multidimensional arrays, and jagged arrays (a multidimensional array where all rows can have a different number of columns). Single-dimensional and multidimensional are the actual array constructs in the CLR, and jagged arrays are implemented using single-dimensional arrays of single-dimensional arrays. See Figure 7 for a graphic representation of these array types.

To assist a profiler in distinguishing the array types and accessing the correct element values, the profiling API provides two functions: ICorProfilerInfo::IsArrayClass and ICorProfilerInfo2::GetArrayObjectInfo. GetArrayObjectInfo helps a profiler determine the structural information about an array, such as the total number of elements and the dimension size and lower bound. IsArrayClass aids a profiler in identifying the type of the array, its elements, and the array rank (number of dimensions). First, let's look at IsArrayClass:

HRESULT IsArrayClass (
[in]  ClassID classID, // class ID for class in which we're interested
[out] CorElementType *pBaseElemType, // describes array element type
[out] ClassID *pBaseClassId, // class ID for array element type
[out] ULONG *pcRank) // number of array dimensions, known as rank

CorElementType is one of the basic enumerations in the unmanaged APIs for .NET that represents all of the types of things that can be encountered. It is similar to the VARIANT VARTYPE definitions from COM, but is not an exact match because .NET specifications differ from COM. Figure 5 shows CorElementType and its values. In IsArrayClass, CorElementType is used for the pBaseElemType parameter.

The first parameter is classId, which is the class we are examining to see if it is an array. The second parameter is pBaseElemType, which indicates the CorElementType of the elements in the array. Next is the pBaseClassId parameter, which gives us the actual class ID for all of the elements in the array. To determine the number of dimensions in our array, we look at the pcRank parameter, which allows us to see how the array is constructed.

Now that we know how many dimensions we are dealing with (from the pcRank parameter), we can use this information to help us set up our parameters for GetArrayObjectInfo. GetArrayObjectInfo looks like this:

HRESULT GetArrayObjectInfo(
[in] ObjectID objectId,       // object ID for array to examine
[in] ULONG32 cDimensionSizes, // length of sizes and lower bounds arrays
[out, size_is(cDimensionSizes), length_is(cDimensionSizes)]
ULONG32 pDimensionSizes[],    // sizes of each requested dimension
[out, size_is(cDimensionSizes), length_is(cDimensionSizes)]
int pDimensionLowerBounds[],  // lower bound of each requested dimension
[out] BYTE **ppData);         // pointer to the raw buffer of array data
Once GetArrayObjectInfo is called, the profiler has the size of each dimension (pDimensionSizes), the lower bound of each dimension (pDimensionLowerBounds), and the offset in memory where the array values start (ppData). With this information, and using the element type from IsArrayClass, the profiler can now walk along the array and examine the values for each element. Once each value is found, it can be examined using the techniques discussed earlier to determine if more drilling is necessary, or if the value is in fact right at the element location.

The final piece of layout information that a profiler needs to decode the parameter and return value information is how to handle boxed values. A value is boxed when a value type is passed to a parameter that requires a reference type. These values are treated as a reference type, where the object reference points to a box on the GC heap which contains the value type. To see where the actual values are located within the box, we use the ICorProfilerInfo2::GetBoxClassInfo method:

HRESULT GetBoxClassLayout(
[in] ClassID classId, // class ID for the boxed type
[out] ULONG32 *pBufferOffset) // offset to the value in the box

GetBoxClassLayout returns information about how to find the value for a boxed type. If you pass a Class ID for a class that cannot be boxed, you will get a failed HRESULT return code. The offset received for boxed items in the pBufferOffset parameter is offset from the Object ID of the boxed item.

As you can see, examining the values of a running program is a complex activity. But the new Profiling APIs let you do just that.

Conclusion

The Profiling API has been updated substantially to help you deal with generics, get stack traces, see new information about threads, and more closely examine the parameter and return value data. C# and managed code has a lot going for it, but every once in a while, you might want to dust off those C++ skills and get under the hood for a look around.

[ Editor's Update - 1/9/2006:

In the final release of the Profiling API for the .NET Framework 2.0, several new items were added. Primarily, these enable inspection in four main areas: static variable evaluation, garbage collection information, exception tracing, and the enumeration of frozen objects.

Static variables are scoped in four ways: RVA, AppDomain, Thread, and Context. By default static variables are scoped by AppDomain, but RVA statics can be created in C++ and MSIL, Thread statics are created using the System.ThreadStaticAttribute, and Context statics are created using the System.ContextStartAttribute. In order to determine which scope a static variable is in, use the ICorProfilerInfo2::GetStaticFieldInfo method to get back a COR_PRF_STATIC_TYPE value that tells in what scope the static resides. Once the scope has been determined, call GetRVAStaticAddress, GetAppDomainStaticAddress, GetThreadStaticAddress, or GetContextStaticAddress to get the address of the value for the static variable.

Methods that deal with gathering garbage collection information have been added to ICorProfilerInfo2 (new callbacks have been added to the ICorProfilerCallback2 interface). For ICorProfilerInfo2, GetGenerationBounds allows the profiler to determine the location in memory of the garbage collector's generations, and GetObjectGeneration allows the profiler to determine the generation and location in the garbage collector for a particular object. The COR_PRF_GC_GENERATION_RANGE structure describes how the generation is laid out in meory. New callbacks on ICorProfilerCallback2 for garbage collection include GarbageCollectionStarted, GarbageCollectionFinished, SurvivingReferences, FinalizeableObjectQueued, RootReferences2, HandleCreated, and HandleDestroyed.

Exception tracing, through the ICorProfilerInfo2::GetNotifiedExceptionClauseInfo and ICorProfilerInfo2::GetCodeInfo2 methods, allows the profiler to determine the IL address for the exception block that handled the exception. The COR_PRF_EX_CLAUSE_INFO value returned from GetNotifiedExceptionClauseInfo provides the information to pass into GetCodeInfo2, which can be used to resolve the location of the given function that scopes the IL address.

Finally, frozen objects are generated at NGEN time. See the System.Runtime.CompilerServices.StringFreezingAttribute documentation in MSDN for an example of how to set up a frozen object. In order to find and inspect them, a profiler uses the ICorProfilerInfo2::EnumModuleFrozenObjects method and the corresponding ICorProfilerObjectEnum interface.

]
Jay Hilyard is a software engineer on the New Product Development team with Newmarket International and co-author of the C# Cookbook (2nd edition) from O'Reilly. When Jay is not busy spending time with his family or watching the Patriots, he can be reached at hilyard@comcast.net.


Interop: Get Seamless .NET Exception Logging From COM Clients Without Modifying Your Code

Interop

Get Seamless .NET Exception Logging From COM Clients Without Modifying Your Code

Matt Adamson
This article discusses:
  • Why exception logging is important
  • How exception logging works in .NET
  • Logging all .NET exceptions thrown to COM clients
This article uses the following technologies:
.NET, COM, C++, and C#
Code download available at: ExceptionLogging.exe (267KB)
Contents
Why Log .NET Exceptions?
How .NET Exceptions are Currently Logged
Exception Information Lost
Workarounds
Hooking
Concurrency Issues
Exception Queue
Exception Logging Thread
The ExceptionPublisher Class
Fallback Logging
ASP
Sample Project
Conclusion

M
any developers who still use ASP pages for presentation services are integrating COM and .NET objects to provide the business services of the application. Using .NET objects from ASP can help developers gain experience with the .NET Framework before migrating to ASP.NET. Their ASP pages make use of the new .NET components through COM-callable wrappers (CCW). But how do they handle exceptions?

Whenever a .NET exception is thrown from .NET components to the ASP page, it's converted into an Err object that contains information on the error. Unfortunately, a wealth of information such as the stack trace, all inner exceptions, and other properties within the .NET exception are lost; only the string description and a number are returned to the ASP page. To allow .NET exceptions thrown to COM clients to be added to the event log, I've written a component that uses techniques such as hooking.

Why Log .NET Exceptions?

Most applications log unexpected exceptions and, in some cases, expected exceptions. The Exception Management Application Block (EMAB) provides a very useful framework for publishing exceptions. You simply call a publish method with the Exception object. This could either be from within a centralized catch handler or from an unhandled exception handler using the UnhandledException event in the AppDomain class (see Figure 1).

Figure 1 Unhandled Exception Handlers (Close Figure 1)
void Start()
{
    AppDomain.CurrentDomain.UnhandledException +=
        new UnhandledExceptionEventHandler(OnAppDomainException);

    try
    {
        ... // perform work of the application
    }
    catch(MyApplicationException exception)
    {
        ExceptionManager.Publish(exception)
    }
}

... work performed elsewhere in application ...

private static void OnAppDomainException(
    object sender, UnhandledExceptionEventArgs eventArgs)
{
    ExceptionManager.Publish(eventArgs.ExceptionObject);
}

Logged exceptions provide a wealth of information when diagnosing problems. For example, if the exception thrown is wrapping another exception, the inner exceptions usually contain a lot of detail to build up the context for the error. The most inner exception usually contains the actual error that caused the failure, such as a database schema that is out of date or a SQL Server™ database that is not available.

Other useful functionality includes the ability to find specific information related to the particular exceptional condition. For example, an ArgumentOutOfRange exception contains the argument value that was invalid.

The runtime's ability to capture a stack trace when an exception is thrown means that you can determine the specific function that failed. For example, if a function such as IsUserInRole fails and can be clearly seen in the call stack, start looking at the user's security rights or security related-code.

Given these advantages, it is therefore valuable to be able to log any .NET exceptions thrown, regardless of whether a .NET or COM client is being used.

How .NET Exceptions are Currently Logged

Most ASP developers use some form of logging component to log errors to a specific destination, such as an event log or flat file. When an error condition occurs, a flag is usually set to indicate the page has failed and that the relevant information should be logged. The only information about the error is present in the Err object. The Err object provides access to a Number and Description property, and the description property contains the most detailed information on the error. Sample ASP code is shown here:

On Error Resume Next
Dim oSupplierInvoice
Set oSupplierInvoice = Server.CreateObject("ECommerce.SupplierInvoice")
oSupplierInvoice.GenerateFromOrder(oPurchaseOrder)
If Err.Number <> 0 Then
    LogPageError "Failed to generate purchase order for supplier invoice,
            SupplierInvoiceID=" & oSupplierInvoice.ID
End If

LogPageError is a fictional method that wraps access to the logging component and adds additional context to the error log, such as the page being called, the user within the session, and the Description and Number from the Err object. If the ECommerce.SupplierInvoice component is written as a COM object, then the description and number will be the only information the component raises, so no information will be lost. However, if the component is written in a .NET language, a CLR exception will have been thrown and converted into an Err object, and much of the useful information in the CLR exception will be lost.

Exception Information Lost

When a COM client uses a .NET component, a COM-callable wrapper is created. The CCW is a proxy to the real .NET object. This object is implemented in either the mscorwks.dll or mscorsvr.dll, depending on factors such as a how many processors the system has or environment variable settings. Either of these DLLs are loaded by mscoree.dll, which acts as a shim to load either of the modules mscorwks or mscorsvr.

When a .NET exception is thrown, the CCW retrieves the .NET exception interface and creates a COM error object based on the information it contains. This error object can typically be queried for the description and the number. The description is taken from the exception's Message property, and the number is taken from the exception's HResult property, if available.

Adam Nathan documented a technique in his excellent book, .NET and COM: The Complete Interoperability Guide (Sams Publishing, 2002) where a .NET exception could be obtained for COM clients by calling GetErrorInfo. This works because the mscorsvr/mscorwks DLL makes a call to SetErrorInfo after the .NET exception is caught within the CCW. This retrieves the last error object set on the current thread. The IErrorInfo interface pointer returned can then be queried for the .NET exception interface _Exception. Here is the C++ code to do this:

try
{
    ... // Work which throws a .NET exception through a CCW
}
catch(_com_error &err)
{
    _ExceptionPtr spException = err.ErrorInfo();
}

You can see here that the .NET exception is simply retrieved by querying the IErrorInfo pointer set on the current logical thread. Unfortunately, this only works for early-bound clients using the specific interface that is defined for the component. Late-bound scripting clients such as ASP pages always use the IDispatch Invoke method to call methods and properties defined on the .NET component. The Invoke method calls GetErrorInfo to retrieve rich error information in order to set the relevant fields of the EXCEPINFO structure. The scripting engine then uses this structure to build the Err object for the ASP page to query.

Since the GetErrorInfo call also clears the error information, it is not available after the late-bound call completes. If this didn't happen, it would in theory be possible to write a method within another C++ COM server that simply called GetErrorInfo and retrieved the .NET exception interface pointer.

Figure 2 .NET Calls Through a CCW
Figure 2 .NET Calls Through a CCW

To see why the scenario fails for late-bound clients, I'll walk through the calling of a managed method from a late-bound COM client. The .NET method call is initiated through late binding in a script engine (VBScript or JScript®), hosted by asp.dll. This results in IDispatch::Invoke being called on the CCW (an EXCEPINFO structure is passed). Next, the CCW invokes the actual managed method on the managed object. In our scenario, this method throws an exception. The CCW catches the exception and calls SetErrorInfo to store the exception's information. The implementation of IDispatch::Invoke sees that an exception occurred and calls GetErrorInfo to retrieve the info it needs to fill the EXCEPINFO structure. Since GetErrorInfo clears the error information, when IDispatch::Invoke returns, the .NET exception is lost. This process is outlined in Figure 2.

Workarounds

A workaround to enable exceptions to be published is to modify all properties/methods in the managed code component to catch all types of exceptions and publish them before rethrowing:

public void DoSomethingUseful()
{
    try
    {
        ... // Do some useful work
    }
    catch(Exception exception)
    {
        ExceptionManager.Publish(exception);
        throw;
    }
}

Clearly, this approach has serious drawbacks. There is a significant development overhead: all .NET methods/properties that could be called by COM clients need to be modified to include a catch handler. In practice, this will mean all public methods/properties, as it is impossible to predict the future use of components.

Another problem with this approach is that if the .NET component is being used by a .NET client, this client may also publish the exception, eventually causing the exception to be published twice. This problem also arises if a public method on the component calls another public method on the component.

Furthermore, catching exceptions of type Exception is not considered good practice for various reasons, cited in the exception handling chapter in Jeffrey's Richter's excellent book, Applied Microsoft .NET Framework Programming (Microsoft Press®, 2002). For example, a StackOverFlow exception could be thrown, in which case the catch handler shouldn't do any more work, such as publishing the exception, that could further add to the stack.

Even if these reasons were not enough, sometimes the source code to the .NET components will not be available, as in the case of third party components. The only solution is to implement a .NET proxy for the component, which simply delegates to the object surrounded by a try/catch block.

Now let's look at how to create a component that provides seamless logging of .NET exceptions when they are thrown to COM clients without requiring any modification to .NET components. Any .NET exceptions that are raised to COM clients through the CCW are automatically logged using the EMAB.

Hooking

In Windows®, functions exported by DLLs can be replaced by other custom functions as long as they provide the same signature. This technique is known as hooking. It works by changing function address pointers within a DLL's import address table (IAT). Each imported API has its own reserved place in the IAT where the address of the imported function is written to by the Windows loader. Once a module is loaded, the IAT contains the address that is invoked when calling imported APIs.

My hooking functions are contained in the HookSupport class, available on the MSDN®Magazine Web site as part of the code download associated with this article. The most important method on this class is HookImportFunctionsByName, which takes the module handle in which the import functions will be hooked, the name of the module being hooked, an array of structures containing the function name being hooked, and the new custom function procedure address. John Robbins, the Bugslayer columnist for MSDN Magazine, often showed how to hook functions in the days of Win32®. I am using the techniques John presented in his debugging book, Debugging Applications for Microsoft .NET and Microsoft Windows (Microsoft Press, 2003).

As mentioned earlier, the reason a .NET exception is not available to ASP clients is that it is initially set as the thread's error object using SetErrorInfo; it is later cleared when the scripting runtime calls GetErrorInfo to build the EXCEPINFO structure required for the IDispatch Invoke method. In theory, if the SetErrorInfo function could be hooked and replaced within another custom function, the .NET exception interface pointer could be extracted and published using the EMAB before the scripting runtime calls GetErrorInfo to clear it.

The SetErrorInfo function is defined in the OLEAUT32 module. Unfortunately, although this module is loaded from mscorwks/mscorsvr, it can't be easily hooked because the OLEAUT32 module doesn't exist in the import address table for mscorwks/mscorsvr; rather it only exists in a separate delay-load IAT. This is because the module is delay loaded: at run time, LoadLibrary and GetProcAddress are called to resolve the function address for SetErrorInfo. The resulting address is then stored in the delay-load IAT so that future calls go directly to the API.

Although hooking relies on modifying the import table for the module, for delay loaded DLLs, this must be done before the first call to that function is made inside the module. Since you do not explicitly load mscorwks.dll, you can't be sure that this has not happened already for SetErrorInfo when you start hooking.

The solution requires a variety of functions from different modules to be hooked in turn. First, I set up initial hooks for the LoadLibrary calls from the OLE32 module. When the module mscoree.dll is being loaded by OLE32 in the hook setup, further hooks are set up for the LoadLibrary calls from the mscoree.dll module. When the module mscorwks.dll is being loaded by mscoree.dll in the second hook setup, additional hooks are set up for the GetProcAddress calls from the mscorwks.dll module. (Note that mscorsvr can also be used sometimes, depending on various factors, including number of processors.) With those hooks in place, when a SetErrorInfo call is made by the mscorwks.dll module hooked using GetProcAddress (either by name or ordinal value), the function pointer to my custom SetErrorInfo is returned, which publishes the exception. This is shown in Figure 3.

Figure 3 Various Hooks
Figure 3 Various Hooks

In the LoadLibrary hooked functions for the modules OLE32.dll and mscoree.dll, the Win32 LoadLibrary call is always made first. This ensures that the module request is loaded and available to be used to install further hooks to LoadLibrary and GetProcAddress calls from these modules. The most important issue to address here is to ensure that the hooks are made early enough within the process that hosts the .NET components.

When a .NET exception is thrown and caught by the CCW, the custom defined SetErrorInfo function is called. The .NET interface pointer can then be extracted from this function by querying for the _Exception interface, as shown here:

HRESULT __stdcall SetErrorInfoWithExceptionLogging(
    DWORD dwReserved, IErrorInfo  *pErrInfo)
  {
       CComQIPtr<_Exception> spException(pErrInfo);
       if (spException)
       {
           // Perform work with .NET exception interface
      }
  }
The calls to the various hooking functions are shown in Figure 4.

Figure 4 Debug Option Showing Hooked Function Calls (Close Figure 4)

Once the functions are hooked they remain hooked until the process terminates. This can cause an access violation if an error is raised within the process after the DLL has been unloaded because the SetErrorInfo hooks are still pointing to functions defined in the DLL which, when unloaded, will point to invalid addresses. To resolve this, the DllCanUnloadNow function returns S_FALSE to prevent the DLL from being unloaded. The alternative approach is to reset the original hooked function pointers. Either approach you choose is fine.

Concurrency Issues

The hooked SetErrorInfoWithExceptionLogging function should be thread-safe. At first, you might want to place .NET object creation and logging in the custom SetErrorInfoWithExceptionLogging function and use a critical section to protect against concurrent access. But consider the following sequence of events. First, Thread A causes an error and enters SetErrorInfoWithExceptionLogging, acquiring a critical section. While Thread A is executing, it tries to acquire another resource held by Thread B, so it waits for this resource to be available. While Thread B holds this resource, it generates an error. The SetErrorInfoWithExceptionLogging function is then called on Thread B, blocking the attempt to acquire the critical section.

Result: deadlock! The code in SetErrorInfoWithExceptionLogging is not directly trying to acquire other resources potentially held by other threads, but if it is required to create .NET objects in it, several events will happen. For example, while creating the .NET object, the system may need to load a DLL. This means obtaining the loader lock for that time interval, which creates the potential for a deadlock. If .NET objects were created and used to publish the exception in the SetErrorInfoWithExceptionLogging, a cross-apartment call would be made from a single-threaded apartment (STA) to a multithreaded apartment (MTA). That could give rise to a reentrant scenario in which SetErrorInfoWithExceptionLogging is called in an STA and the hooked function makes an outgoing COM call to another apartment, which causes the COM libraries to enter the STA message loop. When another COM method call for an object hosted in this STA comes in, it is accepted because we are in the message loop. During the processing of this new request, the SetErrorInfoWithExceptionLogging function is called once again.

Owing to these concurrency issues, the SetErrorInfoWithExceptionLogging function does as little work as possible. It simply adds the exception to a queue that is accessed from the ManagedExceptionLoggerTask worker thread. The ManagedExceptionLoggerTask object performs the logging of the exception, which does the significant work of creating the managed ExceptionPublisher object and publishing the exception. This thread is not subject to the same locking issues, making it a good place in which to publish the exceptions.

Exception Queue

To minimize the blocking issues, a new queue object is created. Managed exceptions are added and removed on a first-in first-out basis. These exceptions will be removed from the queue by another worker thread and then published using the exception management application block.

The type of objects stored within the queue cannot be .NET Exception interface pointers because the exception needs to be accessed across apartments from the worker thread. For example, the custom hooked function could be called on an STA, but the worker thread uses an MTA. In an ASP application, each client will be calling on its own STA.

The global interface table (GIT) stores interface pointers so they can be safely accessed across apartments. Objects added from one apartment can be removed from the GIT and accessed in another apartment. When an interface is added to the GIT, a cookie is returned in a DWORD. This DWORD needs to be stored within the queue so that the worker thread can simply pop the DWORD off the queue and use the GIT to extract the .NET exception object corresponding to this cookie.

The date and time the exception was raised also needs to be recorded within the element in the queue. Consider the following scenario: an error is raised, the custom SetErrorInfo function is called, the exception is added to the queue, and the worker thread is signaled to publish the exception. If the worker thread doesn't get scheduled for some time, even after only a few seconds, other exceptions may get added to the queue before the first is actually published. The actual recorded time in the event log will thus be slightly out of sync with the actual time the error was raised. The recorded time is therefore essential. The recorded time and the .NET exception cookie will be wrapped within a structure, which is added to the queue.

Because the STL queue class is not thread-safe and a small amount of work needs to be done to add an exception to the queue, the ManagedExceptionQueue class was created. This protects simultaneous access to the queue using a class-critical section, it wraps up the use of the GIT into push and pop functions on the class, and provides a nice interface for the worker thread and custom hook function to set and retrieve exception information from the queue. This process is mapped out in Figure 5.

Figure 5 Two Clients Raising Exceptions Through the CCW
Figure 5 Two Clients Raising Exceptions Through the CCW

As soon as the custom SetErrorInfo function adds the exception to the queue, an event is signaled within the worker thread to notify it that an exception can be published. The code for the queue class is found in ManagedExceptionQueue.cpp in the download on the MSDN Magazine Web site.

Exception Logging Thread

A new ManagedExceptionLoggerTask class is created to act as a worker thread. This sits idle waiting for either of two events to become signaled: a new exception has been added to the queue or the thread should terminate. The task should usually be asked to finish when the application is closing down.

The new thread is created after the initial hooks have been set up in the Inject method on the ManagedExceptionInjector COM object. If the thread wakes up because another thread has signaled the Exception Added event, then each exception is retrieved from the queue using the ManagedExceptionQueue pop method. This extracts and returns the interface pointer from the GIT using the stored cookie. Each of the exceptions is then published using the ExceptionPublisher class through a CCW. If the exception publishing fails for any reason, an attempt is made to extract useful information from the exception, such as the stack trace and inner exceptions, and to log this to the event log. If this also fails, the exception information is output to the debug window using the OutputDebugString Win32 function. This exception logging process is shown in Figure 6.

Figure 6 Exception Logging Process
Figure 6 Exception Logging Process

The ManagedExceptionLoggerTask joins an MTA; it performs all the exception publishing in the MTA to avoid cross-apartment calls when the ExceptionPublisher object is created. Managed objects, such as the ExceptionPublisher, that are exposed to COM behave as if they had aggregated the freethreaded marshaler. In other words, they can be called from any COM apartment in a freethreaded manner. The only managed objects that do not exhibit this freethreaded behavior are those objects that derive from ServicedComponent.

An instance of the ExceptionInfo structure is extracted from the top of the queue using the ManagedExceptionQueue pop method. This structure contains the date and time the exception was raised. The date and time is used to create a managed NameValueCollection object with one name value item containing the value of the date. The name of this value is called ExceptionRaisedDateTime; it is only used when displaying the exception in the event log.

The ExceptionPublisher Class

Information in the .NET exception extracted from the hooked SetErrorInfo needs to be logged somewhere, whether in a file, event log, or e-mail message. Specifically, all the information mentioned previously, such as properties of the exception, the stack trace, and any inner exceptions, need to be included in the log.

The Patterns and Practices Group at Microsoft has created the EMAB specifically for this purpose. If you haven't already done so, I strongly suggest you review all other application blocks available to see if they are appropriate to use within your current projects. The exception block is a simple and flexible mechanism for publishing exception information through an ExceptionManager class. It also allows you to create your own publishers to publish data to other data sources such as XML files or a database.

The Publish method in this class needs to be called to publish the exception. This is defined as static, which makes it easy for .NET clients to use; however, it isn't friendly for COM clients, which can't call static methods. A simple solution is to use the facade pattern to wrap access to the class within another class. There are various guidelines developers should adhere to in order to make .NET components easier for COM clients to use. One is to ensure that the class has a default constructor. Two new assemblies were created, which contain the ExceptionPublisher class and IExceptionPublisher interface. The wrapper method within the class is shown in the following code:

public void Publish(Exception exception, NameValueCollection nameValueItems)
{
    ExceptionManager.Publish(exception, nameValueItems);
}

The overloaded Publish method is called, which takes not only the exception object but also a collection of name-value items that can specify other important data when the exception is published. The ManagedExceptionLoggerTask worker thread uses this to add the date and time the exception was raised.

The ExceptionPublisher class also employs a number of other best-practice techniques. This includes using explicit GUIDs for the class and interface, and deriving from a custom interface rather than using the default class interface. To learn more about these techniques, take a look at .NET and COM: The Complete Interoperability Guide. By providing a default constructor and a public non-static method to publish exceptions, I've created an easy way for COM clients to use my class to publish exceptions:

IExceptionPublisherPtr spExceptionPublisher(__uuidof(ExceptionPublisher));
spExceptionPublisher->Publish(spException, spNameValueItems);

Fallback Logging

If the .NET exception cannot be published using the exception block (for reasons such as failure to create the object or inadequate permissions), an attempt should be made to log to the event log directly. If this also fails, the exception should be written to the debug window as a last resort. Interestingly, if the ExceptionPublisher's Publish method fails, two exceptions have to be logged to the event log. The first is the .NET exception raised from the Publish method that contains information on why the Publish method failed, and the second is the actual exception being logged.

The first exception can actually be extracted using Adam Nathan's technique presented in the beginning of this article because an early-bound COM client is being used here to call a .NET component. The .NET exception is extracted directly from the error object by querying for the _Exception interface.

The exception information is added to the event log in the CManagedExceptionLoggerTask::LogExceptionToEventLog method. This method does work in a manner similar to the Publish method, extracting all inner exceptions and writing out a stack trace for each exception. If any errors happen to occur with this logging to the event log, the reason and the exception information are written to the debug window.

ASP

ASP applications usually run in either the dllhost or inetinfo process, depending on the isolation level. For example, medium (which is unavailable on Windows NT®) and high use dllhost.exe, while low always uses inetinfo. Each ASP application using the ManagedExceptionInjector component must not only create the object and call Inject but also store a reference to it within the Application variable. Here is the ASP code to do this, with error handling omitted for brevity:

  Dim oInjector
  Set oInjector = Server.CreateObject(
      "NetInteropServicesEngine.ManagedExceptionInjector")
  Set Application("ManagedInjector") = oInjector

The call to inject all the required hooks within the process needs to be called within the application OnStart event. This ensures that it is called before any other clients in the process cause SetErrorInfo to be called on a CCW; otherwise, the hooks can't be set up. This call should be placed right at the beginning of this event handler before any other code.

Storing the object in the application variable allows us to access it in the application On End event and then call the UnInject method. The object must be apartment-agile. In other words, it must be accessible from any apartment within the process to be able to be stored in the application variable. To do this, the object aggregates the freethreaded marshaler using the CoCreateFreeThreadedMarshaler function.

The application On End event will be called when the Web application ends, either because the Web server has been restarted or the specific application has been unloaded (this is only available to applications running with high isolation). The UnInject method simply signals to the ManagedExceptionLoggerTask worker thread to finish and clean up.

Sample Project

A sample Web project called ManagedExceptionWebApp is available for download from the MSDN Magazine Web site. A main page provides hyperlinks to three other pages. One simply displays text, another adds a couple of numbers together, and a final page calls a .NET object called PurchaseOrder in the MSDN.ExceptionGenerator namespace, which always throws a .NET exception.

In the ThrowNetException ASP page, a managed PurchaseOrder object is created and the GenerateOrderForSupplier method is called. The code for this method is shown in Figure 7. This sample follows a common design pattern: using data access classes to access the data store while leaving the business object responsible for the functions of the business objects.

Figure 7 GenerateOrderForSupplier (Close Figure 7)
public void GenerateOrderForSupplier(String supplierName)
{
    try
    {
        PurchaseOrderData purchaseOrderData = new PurchaseOrderData();
        purchaseOrderData.GenerateOrderForSupplier(supplierName);
    }
    catch(SqlException exception)
    {
        throw new PurchaseOrderException(String.Format(
            "Failed to generate purchase order for supplier {0}",
            supplierName), exception);
    }
}

PurchaseOrderData GenerateOrderForSupplier attempts to log onto a database using a connection that is obviously incorrect, as you can see in the following code snippet:

public void GenerateOrderForSupplier(String supplierName)
{
    using(SqlConnection connection = new SqlConnection(
        ConfigurationSettings.AppSettings["connStr"))
    {
        connection.Open();
        ... // Code to insert purchase order details into database
    }
}

When a SqlException is thrown from the PurchaseOrderData GenerateOrderForSupplier method, it is caught in the PurchaseOrder GenerateOrderForSupplier method. The exception is then thrown again using the custom exception type PurchaseOrderException. This type is used for all exceptions when processing purchase orders. It derives from BaseException, which is a requirement for all exception classes created for use in the EMAB.

To run the Web application, it needs to be installed in a new virtual directory called ManagedExceptionWebApp. The .NET assemblies need to be built, copied into the application folder and registered for COM interop using regasm. The exception block assemblies Microsoft.ApplicationBlocks.ExceptionManagement.dll and Microsoft.ApplicationBlocks.ExceptionManagement.Interfaces.dll also need to copied to this folder. (Note that the Microsoft.ApplicationBlocks.ExceptionManager.dll assembly needs to be installed using the InstallUtil utility to register a number of different event sources before it's used from an ASP page. If this doesn't get run, the ASP application will attempt to create the event sources the first time they're referenced and can, in some configurations, cause a security exception because the anonymous user doesn't have permissions to create event sources. A file called RegisterTypeLibs copies the assemblies from the debug folder and registers them for COM interop.

The application folder for the CLR host where the .NET components are created will be either <windows>\system32 when dllhost is used for applications with protection set to medium or high, and <windows>\system32\inetsrv for applications with low protection. The corresponding configuration files will therefore be called dllhost.exe.config and inetinfo.exe.config if any runtime configuration is required.

Conclusion

In this article I've discussed the design and implementation of an object that intercepts the .NET runtime CCW calls when exceptions are raised. This serves up seamless exception logging without any changes to .NET components. I have also examined various issues I encountered trying to build a robust solution which works well in a production environment.

Not only does the Framework show you a variety of low-level techniques such as hooking and dispatching work to a queue, but it does so by building a useful component which you can put to good use straightaway in your own applications.

Matt Adamson is a senior software engineer at Tranmit plc where he designs and develops n-tier applications using C# and C++. You can reach him at adamson_matthew@hotmail.com.


All About Statics: Get a Charge From Statics with Seven Essential Programming Tips

All About Statics

Get a Charge From Statics with Seven Essential Programming Tips

K. Scott Allen
This article discusses:
  • The performance and behavior of type constructors
  • Static members and thread safety
  • Static classes in the .NET Framework 2.0
  • Best practices for shared members of a type
This article uses the following technologies:
.NET Framework, C#, Visual Basic
Code download available at: StaticsinNET.exe (150KB)
Contents
The Price of Being Precise
The Exception to the Rule
Constructor Locks
Static Reflection
Static Classes in the .NET Framework 2.0
Static Locals
Static Illusions
Static Conclusions

I
t is rare that you would work on a .NET-based application and not encounter a type with a Shared method or static field. Since these abstractions have special behaviors, there are important questions to ask about the implementation. When will the runtime initialize the static fields? Is this method thread safe? Could this class cause a bottleneck?

In this article, I will examine seven characteristics of statics that you should know. I will touch on the finer points of static constructors and how the C# and Visual Basic® compilers work together with the runtime to implement additional safety behind the scenes. By the end of the article, you will come away with best prac-tices for the use of static members and static classes in your apps.

The Price of Being Precise

Type constructors are also known as type initializers. They can initialize the state of a type by initializing static or shared fields to a predefined or computed value. A type will gain a type initializer in two scenarios. The first scenario is when a developer explicitly adds a type constructor using the Shared keyword on a New subroutine in Visual Basic, or by adding a static, parameterless method with the same name as the type in C#. The second scenario is when a type has a initializer for a static field, in which case the compiler adds a type constructor behind the scenes. Figure 1 shows these two scenarios with Visual Basic .NET.

Figure 1 Implicit and Explicit Static Constructors (Close Figure 1)
' This type has an explicit type initializer
Class ExplicitConstructor

    Shared Sub New()
        _message = "Hello World"
    End Sub

    Public Shared ReadOnly Property Message() As String
        Get
            Return _message
        End Get
    End Property

    Private Shared _message As String

End Class

' This type has an implicit type initializer
Class ImplicitConstructor

    Private Shared _message As String = "Hello World"

    Public Shared ReadOnly Property Message() As String
        Get
            Return _message
        End Get
    End Property

End Class

If you compile the classes in Figure 1 and run the resulting assembly through the FxCop tool, you will find that it produces a critical warning about the ExplicitConstructor class. The warning is "Do not declare explicit static constructors." The rule description tells you that an explicit static constructor results in code that performs worse. The recommendation from FxCop is to initialize static fields where they are declared. But instead of taking the recommendation at face value, let's dig into the assembly using a disassembler or decompiler, like ILDASM, to really understand why there is a performance difference.

In Microsoft® intermediate language (MSIL), type constructors will have the name of .cctor (short for class constructor). The .cctor methods for ExplicitConstructor and ImplicitConstructor are shown in Figure 2.

Figure 2 Disassembled Implicit and Explicit Static (Close Figure 2)

Static1.cctor
.method private specialname rtspecialname static
        void .cctor() cil managed
{
  // Code size      13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr   "Hello World"
  IL_0006:  stsfld  string BeforeFieldInitVB.ImplicitConstructor::_message
  IL_000b:  nop
  IL_000c:  ret
} // end of method ExplicitConstructor::.cctor
Static2.cctor
.method private specialname rtspecialname static
        void .cctor() cil managed
{
  // Code size      12 (0xc)
  .maxstack  8
  IL_0000:  ldstr   "Hello World"
  IL_0005:  stsfld  string BeforeFieldInitVB.ExplicitConstructor::_message
  IL_000a:  nop
  IL_000b:  ret
} // end of method ImplicitConstructor::.cctor

The MSIL in Figure 2 does not offer any hints about the performance difference. The compiler has given you almost identical code for the implicit type constructor in ImplicitConstructor and the explicit type constructor in ExplicitConstructor. If you dig a bit further with ILDASM, you will see a difference in the metadata of the two classes:

.class private auto ansi ExplicitConstructor
       extends [mscorlib]System.Object
{
} // end of class ExplicitConstructor

.class private auto ansi beforefieldinit ImplicitConstructor
       extends [mscorlib]System.Object
{
} // end of class ImplicitConstructor

Notice that ImplicitConstructor has an additional metadata flag named beforefieldinit. This flag allows the runtime to execute the type constructor method at any time it chooses, as long as the method executes before the first access to a static field for the type. In other words, beforefieldinit gives the runtime a license to perform aggressive optimizations. Without beforefieldinit, the runtime must run the type constructor at a precise time—just before the first access to static or instance fields and methods of the type. When an explicit type constructor is present, the compilers will not mark the type with beforefieldinit, and the precise timing restrictions lead to the performance drop hinted at by FxCop. This rule holds in both C# and Visual Basic. You can exaggerate the performance variations with the code in Figure 3.

Figure 3 Performance Testing Static Constructors (Close Figure 3)
Module Module1

    Sub Main()
        TestSharedPropertyAccess()
        Console.ReadLine()
    End Sub

    Sub TestSharedPropertyAccess()

        Dim begin As DateTime = DateTime.Now

        For i as Integer = 0 To iterations
            Dim s As String = ExplicitConstructor.Message
        Next

        WriteResult(DateTime.Now.Subtract(begin), _
             "TestStaticPropertyAccess : ExplicitConstructor")

        begin = DateTime.Now

        For i As Integer = 0 To iterations
            Dim s As String = ImplicitConstructor.Message
        Next

        WriteResult(DateTime.Now.Subtract(begin), _
             "TestStaticPropertyAccess : ImplicitConstructor")

    End Sub

    Sub WriteResult(ByVal span As TimeSpan, ByVal message As String)
        Console.WriteLine("{0} took {1} ms", _
            message, span.TotalMilliseconds)
    End Sub

    Dim iterations As Integer = Int32.MaxValue - 1

End Module

You don't need a high-resolution timer to see the difference in speed. On my 2.8GHz Pentium 4, the first loop (with ExplicitConstructor) executes approximately eight times slower than the second loop (with ImplicitConstructor). The checks that the runtime performs in order to run the type initializer at a precise time adds overhead inside of the loop, while beforefieldinit relaxes the rules and allows the runtime to hoist these checks outside of the loop.

The code in Figure 3 is a worst-case scenario. You'll need to evaluate the tradeoffs in using an explicit type constructor. Few classes will actually need the type constructor to execute at a precise time, so in most cases it makes sense to allow the compiler to add the beforefieldinit flag and avoid explicit type constructors.

The Exception to the Rule

Another behavior unique to type constructors is how the runtime manages exceptions in a type constructor. When an exception is thrown, the runtime will begin to look for the nearest catch clause whose filters specify that it can handle the exception. Now, consider the C# console mode program in Figure 4.

Figure 4 Static Constructor Exceptions (Close Figure 4)
using System;
namespace Exceptions
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            string message = String.Empty;

            for(int i = 0; i < 2; i++)
            {
                try
                {
                    message = ExceptionFromStaticCctor.StaticMessage;
                }
                catch(ApplicationException e)
                {
                    Console.WriteLine("Caught ApplicationException: ");
                    Console.WriteLine(e.Message);
                }
                catch(Exception e)
                {
                    Console.WriteLine("Caught Exception: ");
                    Console.WriteLine("   " + e.Message);
                    Console.WriteLine("   " + e.InnerException.Message);
                }
                finally
                {
                    Console.WriteLine("Message = {0}", message);
                }
            }
        }
    }

    class ExceptionFromStaticCctor
    {
        static ExceptionFromStaticCctor()
        {
            Console.WriteLine("In ExceptionFromStaticCctor cctor");
            throw new ApplicationException("ExceptionFromStaticCctor
                always throws!");
        }

        public static string StaticMessage
        {
            get { return "Hello World"; }
        }

        public string InstanceMessage
        {
            get { return "Hello World"; }
        }
    }
}

The program will produce the results in Figure 5. There are two behaviors to note. First, the program did not hit the catch clause for an exception of type ApplicationException, even though that's the type of exception thrown from the static constructor. The runtime will stop any exceptions trying to leave a type constructor and wrap the exception inside of a new TypeInitializationException object. The original exception thrown inside the type constructor is then available from the InnerException property of the TypeInitializationException object.

Figure 5 TypeInitializationException (Close Figure 5)

One reason this special behavior might occur is that the second time you try to access a static property of the type, the runtime does not try to invoke the type constructor again, but throws the same exception observed in the first iteration. The runtime does not give a type constructor a second chance. The same rules hold true if the exception is thrown from a static field initializer. As you saw earlier, the code for a static field initializer executes inside an implicit type constructor.

A TypeInitializationException can be a fatal error in an application since it renders a type useless. You should plan to catch any exceptions inside of a type constructor if there is any possibility of recovering from the error, and you should allow an application to terminate if the error cannot be reconciled.

Constructor Locks

Type constructors have an additional quirk. This comes from the Common Language Infrastructure (CLI) spec that guarantees a type constructor will execute only once, unless explicitly invoked by user code. To enforce this guarantee in a multithreaded environment requires a lock to synchronize threads. A runtime thread must acquire this lock before invoking the type constructor.

A program taking a lock must take great care not to create a deadlock situation. To see how it can happen, try to put the runtime into a deadlocked state using the sinister code in Figure 6. This sends thread A into the type constructor for Static1, and thread B into the type constructor for Static2, then puts both the threads to sleep. Thread A will wake up and need access to Static2, which B has locked. Thread B will wake up and need access to Static1, which A has locked. Thread A needs the lock held by Thread B, and Thread B needs the lock held by Thread A. This is a classic deadlock scenario.

Figure 6 Attempting to Deadlock with Static Constructors (Close Figure 6)
using System;
using System.Threading;

namespace TypeConstructorLock
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            Thread threadA = new Thread(new ThreadStart(TouchStatic1));
            threadA.Name = "Thread A";

            Thread threadB = new Thread(new ThreadStart(TouchStatic2));
            threadB.Name = "Thread B";

            threadA.Start();
            threadB.Start();
            threadA.Join();
            threadB.Join();
        }

        static void TouchStatic1() { string s = Static1.Message; }
        static void TouchStatic2() { string s = Static2.Message; }
    }

    class Static1
    {
        static Static1()
        {
            Console.WriteLine("Begin Static1 .cctor on thread {0}",
                Thread.CurrentThread.Name);
            Thread.Sleep(5000);
            Console.WriteLine("Static1 has a message from Static2: {0}",
                Static2.Message);
            message = "Hello From Static1";
            Console.WriteLine("Exit Static1 .cctor on thread {0}",
                Thread.CurrentThread.Name);
        }

        static public string Message { get { return message; } }
        static string message = "blank";
    }

    class Static2
    {
        static Static2()
        {
            Console.WriteLine("Begin Static2 .cctor on thread {0}",
                Thread.CurrentThread.Name);
            Thread.Sleep(5000);
            Console.WriteLine("Static2 has a message from Static1: {0}",
                Static1.Message);
            message = "Hello From Static2";
            Console.WriteLine("Exit Static2 .cctor on thread {0}",
                Thread.CurrentThread.Name);
        }

        static public string Message { get { return message; } }
        static string message = "blank";
    }
}

The application in Figure 6 does not deadlock, but instead produces the results that are shown in Figure 7. It turns out the CLI specification also guarantees that the runtime will not allow type constructors to create a deadlock situation, unless additional locks are explicitly taken by user code.

Figure 7 CLI and Deadlock Avoidance
Figure 7 CLI and Deadlock Avoidance

In the program run shown in Figure 7, the runtime avoided deadlock by allowing Static2 to access the static Message property of Static1 before the type constructor for Static1 finished execution. The message from Static1 should have been "Hello From Static1," but instead you see that the message is "blank". This program also demonstrates how static field initializers execute before the code inside an explicit type constructor.

You might recognize that this problem is similar to the C++ static initialization fiasco. The rule of thumb here is to avoid touching the static members of another type from within a type constructor. Although the chances of seeing the previous scenario are slim, the effects would be difficult to debug and track down since there are few diagnostics available.

Static Reflection

The ability to examine the metadata of a type or object instance at run time is a powerful feature. Reflection enables you to build tools like object browsers and to create extensible applications with the ability to plug in types at run time. Using reflection techniques on static members of a type differs slightly from reflecting on instance members of an object. For example, the code in Figure 8 is going to get the value of the static property Message from the type Static1. It will also try to get the same property value using a Type reference for Static2.

Figure 8 Reflection on Static Members (Close Figure 8)
using System;
using System.Reflection;
namespace Reflection
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            Type type;
            object result;
            PropertyInfo[] properties;

            object instance = null;
            object[] index = null;

            type = typeof(Static1);

            properties = type.GetProperties(
                BindingFlags.Static | BindingFlags.Public);

            result = properties[0].GetValue(instance, index);
            Console.WriteLine(result.ToString());

            type = typeof(Static2);

            properties = type.GetProperties(
                 BindingFlags.Static | BindingFlags.Public |
                 BindingFlags.FlattenHierarchy);

            result = properties[0].GetValue(instance, index);
            Console.WriteLine(result.ToString());
        }
    }

    class Static1
    {
        static public string Message { get { return message; } }
        static string message = "Hello World";
    }

    class Static2 : Static1 {}
}

When the code fetches the properties for the Static1 type using GetProperties, it passes binding flags to indicate it wants static members with public visibility. These flags are enough to get the metadata needed about the Message property in the array of PropertyInfo objects. Next, the GetValue method of the first PropertyInfo object in the array is invoked (in this case it is the PropertyInfo for the Message property). Typically, you need to pass an instance of an object as the first parameter to GetValue, but since this is a static member, there is no object to pass. Instead, you can use a null reference for the instance when working with a static property.

Finally, try to get the value of Message using the Type object for Static2. Static members are not inherited, even though IntelliSense® and the C# compiler will produce the illusion of inheritance, which I'll discuss later. To reach the Message property in this case, you need an additional binding flag, FlattenHierarchy, which tells the runtime to include base types in the search for static members.

Static Classes in the .NET Framework 2.0

The classes used so far have a couple of design flaws. Even though these classes have no instance members, you could still create instances of the classes using the new operator in C# or Visual Basic. In addition, someone could inherit from these classes, even though you never intended these classes to serve as base classes.

The solution to the inheritance problem is to apply the sealed keyword in C# or the NotInheritable keyword in Visual Basic. To prevent creation of the classes from code external to the class (although it could still be instantatied by a member of the class), you could add private default constructors to the classes. With C# 2.0, the solution to both of these problems is to apply the static keyword at the class level, as shown in Figure 9.

Figure 9 Static Classes in C# 2.0 (Close Figure 9)
using System;
using System.Collections.Generic;
using System.Text;

namespace StaticClass
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Static1.Message);

            // error: cannot declare variable of type Static1
            Static1 s;

            // error: cannot create an instance of Static1
            object o = new Static1();
        }
    }

    static class Static1
    {
        public static string Message
        {
            get { return _message; }
        }

        static string _message = "Hello World";

        // error: cannot have an instance constructor
        Static1() { }
    }

    // error: cannot derive from a static class
    static class Static2 : Static1 { }
}

The new syntax allows the compiler to enforce a number of new rules. You cannot declare a variable of type Static1 because it is marked as a static class. You also cannot derive from Static1, or add any non-static members to the class. Note also that it's an error to derive a static class from any class other than object.

When you upgrade to the .NET Framework 2.0, you should review your C# classes that have no instance methods. These classes will be candidates for using the static keyword on the class declaration. The additional compile time checks outlined earlier in this article will make the review worth the effort. In the current beta, Visual Basic does not support the Shared keyword on a class declaration. But Visual Basic still has some tricks not available in C#.

Static Locals

A little-known feature in Visual Basic is the support for static variables inside a Sub or Function. Static local variables retain their value between method calls. In this sense, they act just like a class-level variable, but their visibility is limited to a single method.

Static locals were available in versions of Visual Basic before .NET, so presumably this feature is present to simplify the porting of legacy code. Although C and C++ programmers are familiar with static local variables, C# did not carry this feature forward. The static local variable is interesting because the common language runtime (CLR) does not support static variables inside a method. Nevertheless, the code in Figure 10 will compile, execute, and produce the expected results by printing "Count = 1" to "Count = 10."

Figure 10 Visual Basic .NET Statics (Close Figure 10)
Module Module1

    Sub Main()

        Dim i As Integer
        Dim f As New Foo

        For i = 1 To 10
            Console.WriteLine(f.GetMessage())
        Next
    End Sub

End Module

Public Class Foo

    Public Function GetMessage() As String

        Static Dim i As Integer = 0
        i = i + 1
        Return "Count = " + i.ToString()

    End Function

End Class

Since the CLR does not support static locals, the Visual Basic compiler must do additional work to make the program successful. The compiler adds two variables to the class with special names, as can be seen in the ILDASM in Figure 11. The first field in this example is an Integer field with the name of $STATIC$GetMessage$200E$i. This field will hold the value of the static local variable i in GetMessage. The second field, of type StaticLocalInitFlag, assists in initializing the variable i correctly the first time the method executes on each instance of class Foo. It is important to note these fields are not shared and that each instance of Foo will have a new local variable i to initialize and work with.

Figure 11 Visual Basic .NET Compiler Support for Statics (Close Figure 11)

Initialization of a static local requires a bit of work. If you looked at the MSIL for the GetMessage method, you would find 62 instructions (compared to 13 instructions for a similar method that doesn't use only a class-level field). Most of these instructions are in place to check for the initialization of i, and to perform a thread-safe initialization using a lock.

Given the runtime overhead in using a static local variable, it would be wise for you to avoid the feature in current designs. If you have ported code from Visual Basic 6.0 to Visual Basic .NET with static local variables, you should definitely consider refactoring the variable into a class-level variable, which should be a relatively straightforward task.

Static Illusions

Static local variables are not the only compiler trick around. As I alluded to earlier, the CLR does not support the inheritance of static members. Nevertheless, both the Visual Basic and C# compilers allow you to touch static members of a base type through a derived type name. In addition, Visual Basic .NET allows you to touch a static member through an instance of a type, even a derived type, as you can see in the code in Figure 12.

Figure 12 Accessing Static Members from Instances (Close Figure 12)
Module Module1

    Sub Main()

        Console.WriteLine(Static2.Message)

        Dim s As New Static2
        Console.WriteLine(s.Message)

    End Sub

End Module

Class Static1

    Public Shared ReadOnly Property Message() As String
        Get
            Return "Hello World"
        End Get
    End Property

End Class

Class Static2
    Inherits Static1

End Class

An examination of the MSIL for the Main method in Figure 12 will show that the Visual Basic compiler maps both statements with the Message property into a call to Static1::get_Message, completely circumventing the Static2 class. Even though you won't use the instance of Static2, the compiler will still create the new object so as not to miss any side effects of invoking the constructor.

Using a static member through an instance variable should be discouraged. In fact, the current beta of the Visual Basic compiler in Visual Studio® 2005 will produce the compiler warning, "Access of shared member through an instance." In C# it causes a compiler error if you use a static member of a class through an instance variable; however, you can still reach a static member through a derived type class name using the exact same trick that the Visual Basic compiler uses.

Static Conclusions

There are trade-offs to evaluate when adding static members to a type at both design time and run time. At design time, you need to carefully choose which members to mark as static and avoid losing an object-oriented design. Try not to lump diverse functionality into a class by creating a dumping ground for unrelated static methods. If a class contains only static members, remember to mark the class as static or seal the class (NotInheritable) and give the class a private constructor to avoid instance creation.

You should also remember that static classes that preserve state between method calls should be made thread safe by default. Making a class thread safe requires additional care during the implementation and testing of the class. Before going down this road, ask yourself if the extra overhead is absolutely necessary.

Thread safety also has performance implications at run time. In this article, you've seen some performance consequences when a class has a static constructor. Performance implications are particularly important to evaluate when you are writing code for a reusable library. You never know when someone might use your class in a worst-case scenario, like the one that is described at the beginning of this article.

Finally, document your static classes well. Shortcuts to common operations with static methods, such as the static methods Open and Copy on the System.IO.FileClass, will generally be well understood. As soon as you implement a nontrivial class maintaining state between method calls, consumers of the class will want to know details about thread safety and performance. The more you can say up front about a class, the less time you may spend troubleshooting problems later.

K. Scott Allen is the Chief Software Architect for Medisolv Inc. in Columbia, MD. Scott is also a founder of the .NET community site OdeToCode.com. You can reach Scott at scott@OdeToCode.com, or read his blog.


Editor's Note: Celebrating Our 20-Year Milestone

Editor's Note
Celebrating Our 20-Year Milestone

The world of software development is all about milestones. At the lowest level, a programmer has to create a certain set of functions in a fixed amount of time, and is then judged on this performance. Companies bet millions of dollars that they will hit their project milestones and ship their products on time. The very concept of "Internet time" suggested that milestones were compressed. Technically, though, the milestones themselves would always be the same distance apart, hence the word "milestone." It's the speed at which people get to each milestone that's at issue.

MSDN Magazine is approaching its own important milestone. The issue that you hold today, Volume 20 Number 1, marks our 20th anniversary year—we gave birth to Microsoft Systems Journal in 1986.

A lot has changed in the past two decades. Just to give you an idea of how far we've come, let's look back on those golden days of the first MSJ. In 1986, we were covering the latest trends for developers: the new DDE protocol, the latest programmable graphics processors, and a dialog editor for Windows 1.0. Aldus was moving PageMaker to Windows, TSR development was becoming standardized, CodeView was the hot new debugging tool, and XENIX ran on the new Intel 803 chip.

Even more exciting was the news in 1986 that IBM was producing the first PC based on the Intel 80286 chip. It had enough horsepower to run VisiCalc, or WordStar, or whatever it was people did in offices back in the days before solitaire and instant messaging.

Today, of course, we're benefiting from 20 years of technological advances that have given us a world of computing that would have been unimaginable even in the mid-1980s. Multiprocessor machines can fit under your desk. You can run SQL Server on a three- pound laptop. You can take crummy photos with your cell phone and send them anywhere you like for only 25 cents a shot. Advances in graphics processing allow you to play Ms. Pac-Man on your home PC instead of relegating it to just a few high-end arcades and bus stations.

But even though we have incredible computing power at our fingertips, we face new challenges. In 1986, you had to look at every line of code to make sure your program's performance didn't take down your PC. Today, there's little chance that a perf bug would have that big an effect, but so many things can run simultaneously that performance is still an issue that separates good programmers from great ones.

Other things we care about have changed as well. The Internet has grown from 10,000 hosts to about 200 million hosts—who can keep track anymore? Almost every person in the U.S. has access to the Internet, and the numbers worldwide are growing apace. When you step back and look at where we started, and how far we've come, it's an awesome thing. (And this is one of the few times when the word "awesome" is actually appropriate.)

The more we look back at the year of our publication's birth, the more clearly we can see the evolution. Back in 1986, the Fox Network had just launched. "Diff'rent Strokes" was ending its triumphant eight-year run. The Olsen twins were helpless toddlers, and the Red Sox were losing yet another heartbreaker of a World Series. Today, Fox is the unparalleled voice of cheesy reality shows, "Diff'rent Strokes" lives on only in our fond memories, the Olsen twins are America's sweethearts, and the Red Sox again lost a heartbreaker of a...what? They actually won something?

This year, we'll have more celebration of our 20th year serving you. We couldn't have made it this far without your loyal support, and we thank you from the bottom of our hearts for making us the great magazine we've become.

—J.T.

Thanks to the following Microsoft technical experts for their help with this issue: Shawn Burke, Shawn Farkas, Matthew Gibbs, Jim Hogg, Rick James, Atsushi Kanamori, Michael Kaplan, Shon Katzenberger, Jonathan Keljo, Gerrard Lindsay, Eilon Lipton, Rico Mariani, Adam Nathan, Erik Olson, Joel Pobar, Anil Ruia, Anson Tsao, and Maura Van Der Linden.

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

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


New Stuff: Resources for Your Developer Toolbox

New Stuff
Resources for Your Developer Toolbox
Marnie Hutcheson

Contents
Plug-ins for Visual Studio .NET
UI Tools for Visual Studio .NET
Let Your App Create and Manage Images
Manage and Monitor Non-Windows Clients
Partition Backup and Recovery
Microsoft Reporting Services in Action

Plug-ins for Visual Studio .NET

Spices.VSIP.NET (Visual Studio Integration Pack), released by 9rays, is a set of plug-ins for Visual Studio® .NET that let you to add obfuscation, verification, modeling, and debugging to your assemblies built within the Microsoft® .NET Framework. And you can do all of this without even leaving Visual Studio.

When your .NET assemblies are decompiled to Microsoft intermediate language (MSIL), your code can be disassembled, studied, and altered by third parties. An obfuscator is an essential tool for protecting your code from someone infringing on your intellectual property rights.

Spices.Obfuscator also reduces your assembly size and optimizes the performance of your application. It lets you change your obfuscation properties, generate debuggable assemblies, and view the results of your obfuscation.

Your Spices.VSIP.NET project is updated automatically whenever configuration changes are made to a Visual Studio project. You can perform auto-obfuscation immediately after compilation is complete so that the assemblies can be deployed to devices being tested or directly to CAB files.

You can verify your assemblies with the Spices.Investigator which lets you view metadata and PE files for newly generated assemblies, and you can browse your assemblies with the Spices.Informer. Spices.Modeler offers model building features and provides a wide range of live diagrams that can be stored to file. Documents generated by Spices.NET (diagrams, decompiler outputs, and so on) are fully integrated into the Visual Studio environment.

If you have to reverse engineer a project, the Spices.Decompiler decompiles in six languages: IL (same functionality as the Microsoft ILDASM utility), C#, C++, Visual Basic® .NET, J#, and Delphi.Net.

Price: Free demo version is available online. Spices.VSIP.NET Obfuscator (includes Obfuscator, Investigator, Modeler, and Informer plug-ins) is $292.95.

www.9rays.net

UI Tools for Visual Studio .NET

Mercury-UI Enterprise Suite and Mercury-UI for ASP.NET, released by Coalesys, is a collection of DLLs and tools that let you create menus and collapsible panels for Web user interfaces that behave much like their Windows counterparts.

The two main tools are WebMenu and Panelbar. These tools produce dynamically resizable layouts with generated source code that you can extend and render as either server-side DLLs or client-side JS files.

WebMenu lets you create DHTML hierarchical menus. PanelBar lets you create a versatile DHTML navigator with expandable/collapsible groups. Both controls automatically detect and render a tailored DHTML menu for Microsoft Internet Explorer 4.0 and above, Netscape Navigator, Mozilla, and other Gecko-based browsers running on Windows®, Macintosh, and Linux.

The Mercury-UI Enterprise Suite is a multi-platform UI toolkit that includes each server control for ASP.NET, ASP, JSP, and standalone HTML development. The Mercury-UI for ASP.NET toolkit has only the components that are specific to .NET: WebMenu for ASP.NET and PanelBar for ASP.NET, and their related tools.

Mercury-UI Enterprise Suite includes server controls for creating menus and sidebars, as well as standalone applications for managing state or compiling static JavaScript versions of each configured control. You can save the state files or compile a control's DHTML into external JS files that can be used on any Web server or HTML project. The Studio programs also generate server-side ASP.NET code that can be pasted into your page.

Once you add the JavaScript code to your page, the component automatically detects the browser type at run time and downloads only the browser-based JS file for that specific browser. Thus, you get the best possible bandwidth savings, and the JS file will be cached at the browser and reused.

Mercury-UI Enterprise Suite includes both client-side and server-side technologies. If a project is best suited for client-side development, then use the included Studio design-time applications for creating and compiling JS files. If a project is best suited for server-side programming, use the included runtime objects for the supported platforms, natively built as .NET Assembly DLLs, COM DLLs, or JavaBeans.

Price: $499 for a single developer license.

www.coalesys.com

Let Your App Create and Manage Images

ImageGear for .NET, from Accusoft, includes an impressive number of editing and correction features that let your application read, write, and convert images in 23 of the most popular image file formats, and it supports a variety of color spaces and image processing options. In addition, your app has full access to image file format metadata.

The toolset supports all TWAIN versions (including, version 1.9, the latest) and as a result can be used to develop applications that acquire images from scanners, cameras, or frame-grabbers that support the TWAIN interface.

ImageGear for .NET has an add-on PDF module that lets you read and write PDF documents using Adobe PDF libraries. There is also an optional module for reading and writing JPEG2000 format images.

ImageGear for .NET comes with a context-sensitive help menu and documentation that is fully integrated with Visual Studio .NET so you can learn details on each function while you are working. There are samples for Windows Forms (C# or Visual Basic .NET) and ASP.NET Web Forms, PDF reading and writing, and TWAIN scanning. There are also samples of the core functions and formats, image processing, thumbnail creation, and display operations.

Built with 100 percent .NET managed code, there are no outside code dependencies so you don't need to include extra DLLs or ActiveX® controls that have to be registered before you can deploy your app.

Price: Available online for $1,495 per license.

www.accusoft.com

Manage and Monitor Non-Windows Clients

VMX 1.0, released by Vintela, is a Microsoft Systems Management Server (SMS) 2003 administration console snap-in and client that provides an extensible framework for managing and monitoring non-Windows clients natively from within SMS 2003. VMX extends Microsoft Systems Management Server 2003 capabilities to manage UNIX, Linux, and Mac OS X environments.

VMX uses a WBEM/CIM-based architecture, which allows the client to perform like an SMS Advanced Client extended to non-Windows resources. VMX uses the same datastores, classes, objects, and attributes that SMS 2003 uses, so non-Windows systems integrate seamlessly within the SMS administration console.

VMX will extend non-Windows system management capabilities to SMS 2003 for system discovery, software distribution, hardware and software inventory, collections and queries, and reports.

For Microsoft Enterprise Agreement (EA) customers, first-level product support for VMX is provided directly by Microsoft Product Support Services (PSS). This single point of contact means that Microsoft EA customers using SMS 2003 will receive product support for VMX directly from Microsoft.

Price: An initial base pack is $1,995.

www.vintela.com

Partition Backup and Recovery

Partition Table Doctor 2.0, available from the PTDD Group, is a PC partition backup and recovery tool for when you need more than the standard disk scan. When you experience a drive error (other than hardware failure) this versatile tool automatically checks and repairs the Master Boot Record, partition table, and the boot sector of the partition with an error, to recover the bad partition. It can create an emergency floppy disk or a bootable CD to recover the bad partition.

Price: $35.

www.ptdd.com

Microsoft Reporting Services in Action

A Guide to Microsoft SQL Server 2000 Reporting Services by Teo Lachev (Manning Publications) explores SQL Server™ 2000 Reporting Services, the new server-based reporting solution for authoring, managing, and distributing reports.

It can create and manage both paper-oriented and interactive Web-based reports. Until now, it has languished as an enigmatic add-on to SQL Server, its capabilities largely untapped due to the dearth of documentation on how to make it work.

This book shines a bright light on the subject. It not only fills in lots of blanks in the existing product documentation, it presents you with a best-practice approach to report writing that will help you develop valuable reporting solutions, not just simple reports.

The "how to" sections of the book are clearly written with many diagrams and screen shots to provide a solid foundation for using the built-in tools to create reports. And there are lots of real-world examples.

The roadmap that the book follows will probably take you as far as you want to go in tapping the very powerful features of this heretofore overlooked product—from creating simple reports to creating and exporting dynamic interactive Web-based reports and Web services.

In addition to the nuts and bolts of reports, there are chapters dealing with security topics, testing and debugging your reports, data mining, performance, and scalability.

Price: $49.95, softbound, 656 pages.

www.manning.com/lachev

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

Send your New Stuff to newstuff@microsoft.com.

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


Web Q&amp;A: Caching and Expiration, Connection Pools, and More

Web Q&A
Caching and Expiration, Connection Pools, and More
Edited by Nancy Michell

Q Is there a trick to using .js and cascading style sheets (CSS) includes so they're cached instead of fetched when a request is made?

A To accomplish that kind of caching you can enable content expiration using the HTTP Headers property sheet in the IIS administrative snap-in by selecting the Enable Content Expiration checkbox. This will tag the HTTP response using an Expires header (see Enabling Content Expiration). You can also use the Cache-Control header to achieve the same effect if you're using ASP, ASP.NET, PHP, and so forth.

Microsoft® Internet Explorer supports the META tag which can be used for a similar purpose, but the HTTP Response Header is still the preferred way to go. With it, no HTML content needs to be updated and the setting can be applied globally on a Site, Virtual Root, or file basis in IIS.

For an example of a site that behaves pretty well with regard to object caching, use an HTTP debugging proxy such as Fiddler, which logs all HTTP traffic between the client and Web server, and visit office.microsoft.com. When you look at your traces in Fiddler, in the EXPIRES column most objects have an Expiration set and are not rerequested. Figure 1 shows the list view on the left-hand side of Fiddler. You'll note that Office Online either sends an HTTP/1.0 EXPIRES date (for example, Mon, 01 Nov 2004) or an HTTP/1.1 Cache-Control (for example, max-age=86400) header on transactions.

Figure 1 Fiddler in Action (Close Figure 1)

Q Is there a recommended size limit for view state data that will help me avoid significant performance degradation?

A When you consider the user's experience when downloading large amounts of view state data, you realize that the user's pain threshold is really the limiting factor and that it's crossed before the server has problems. See the section entitled View State at Chapter 6 — Improving ASP.NET Performance.

It is very difficult to say what a large view state is without considering the server specs, network architecture, and the client. The blog post at Don't let the BinaryFormatter get at it! shows how the LosFormatter and the BinaryFormatter are used for view state and how the type being serialized impacts the view state size.

The follow-up post at Whidbey will brings us a shorter viewstate, guaranteed! shows how this changes in the .NET Framework 2.0.

Information on the new and improved View State can also be found in an article from the October 2004 issue of MSDN®Magazine, "Speed Up Your Site with the Improved View State in ASP.NET 2.0".

Q I had an issue recently where all machines in a layer (about 18 of them) recycled W3wp (the worker process) more or less simultaneously. I am wondering how the timer was in sync across all these different machines. I'm using Application Center and performed a content push recently.

A The timer starts when the app pool is started. It's common practice to recycle app pools after a significant change to the application (the content push), and this would likely have been automated across a cluster, thus all pools were started at about the same time.

Q How many connection pools can a process create in ADO.NET? I know that if I have n connection strings, I'll have n pools. But is there a maximum value for n? Also, MSDN documentation states that once created, connection pools are not destroyed until the active process ends. Maintenance of inactive or empty pools involves minimal system overhead. Will the inactive pool be pruned?

A If you're asking how many pools may be created, the answer is one pool per connection string used per app domain. Sub-pools are created for integrated security connections based upon the number of different users. If you use n connection strings in a given app domain, you will have n pools. Connection pools are created per app domain, not per process. Pools are created on demand by user connection requests. There is additional logic to prune the pools and even delete the pools after periods of inactivity if the user does not specify otherwise.

There is no upper limit on how many pools can be created. The MSDN comment you mentioned is correct for the Microsoft .NET Framework 1.0 and 1.1. The pools will remain, though they will be empty after inactivity if you did not specify a minimum pool size. In the .NET Framework 2.0, after a period of inactivity the Framework will delete the pool.

Q I have a question concerning the expiration date of HTML pages (in Windows® XP with Microsoft Internet Explorer 6.x). In Expire1.htm, I specified that the page expires immediately:

<html>
<head>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="expires" CONTENT="-1">
</head><body>
<a href="http://HtmlPage1.htm">HtmlPage1.htm</a>
</body>
</html>

Using Internet Explorer, if I browse to Expire1.htm (using HTTP, not HTTPS), after I click on the link I am sent to HtmlPage1.htm. Now if I change the content of Expire1.htm and then click on the Back button, I get the old content of Expire1.htm rather than the new content. In Internet Explorer I selected the option to "check" for newer versions of stored pages on every visit to the page.

A This behavior is by design. By using the META header you are relying on MSHTML to decide whether or not to remove the content from the cache, and the removal of the content from the cache is only performed by MSHTML when the content is downloaded over Secure Sockets Layer (SSL). Since you are using the Forward and Back buttons, you are not really navigating; you are simply displaying the content that was previously downloaded. If the connection is not an SSL connection, then MSHTML simply sets the Expiration header for the content that is stored in the cache.

Those actions rely on whether or not WININET cached the files on the download, which you already discovered by setting the headers on the entire site. If you control the IIS Server or the Web server that is hosting the HTML files, then you can set the Expires Header on a Site, Directory, or File basis, so you could go down to the file level to achieve appropriate caching behavior.

After you navigate from Expire1.htm to HtmlPage1.htm, but before you click on the Forward and Back buttons, you need to look at the Tools | Internet Options | Settings button | View Files button. In the display, find Expires1.htm and look at the Expiration column. Is there a date/time that reflects when you accessed the file? If so, then the rules shown in Figure 2 apply for the cached content when using the Forward and Back buttons. You can see that when using the Forward and Back buttons synchronizing only happens if the content has not been downloaded during the current Internet Explorer session.

Figure 2 Caching Rules (Close Figure 2)

Syncmode | No Expiration Date | Expiration Before Last-Accessed Time | Expiration After Last-Accessed Time
Always | CS | DS | CS
Never | DS | DS | CS
Per-Session | CS | DS | CS
Automatic | SURL | DS |
DS = Don't synchronize the content. CS = Conditional synchronize if Last Checked time was before start of the current Internet Explorer session. SURL = Don't synchronize if URL is marked static; otherwise fall back to per-session.

Now to get the behavior that you intended, instead of using the Forward and the Back buttons to access Expire1.htm after loading HtmlPage1.htm, click in the Address Bar and manually enter in the full path to Expire1.htm. That should result in the new content being displayed because now the caching rules are based on a Hyperlink Click or Address Bar Navigation. In this case the Expiration of the content will determine whether the content should be downloaded again.

Q If an app makes 50 simultaneous connections to SQL Server™, there will be 50 connections in the connection pool. When are they removed from the pool? If their ConnectionLifetime is 0, which the documentation describes as using the maximum connection lifetime, how long is this?

A The logic behind removing connections from the pool is undocumented, but this has not prevented people from trying to discover it and write articles about it. The best one is probably SqlClient Connection Pooling Exposed - Reflection allows .NET developers to peer into the internals of SqlClient. You can also check out the following blog: ADO.NET The misunderstood "Connection Lifetime" managed pooler connection string keyword.

The basic idea is that connections left idle in the pool will be cleared at a random time between approximately four minutes and eight minutes, depending on implementation details. There is nothing that you can do short of using reflection, as shown in the article mentioned previously, to change this default.

The actual behavior for a connection with minimum pool size set is the same as for a connection without it. On closing the connection, the connection lifetime will be checked and it will be disposed. The managed pooler will notice that you have gone under the minimum pool size and it will open a new connection on a background thread. Connection lifetime has nothing to do with the connection pool idle time. Setting it to 0 equals the maximum value of int32.MaxValue, or 2147483647 seconds.

Q Does ADO.NET do anything to secure the connection string parameter passed to the SQLConnection object? I'm concerned about a memory dump exposing a connection string. When a comparison of connection strings is performed to determine if a connection from the pool can be used, is the actual string passed?

A First, it should be noted that the concern about exposing connection strings is not unique to ADO.NET; this is a valid concern for all database access technologies. There is no real way to protect the password from a user who has administrator access to the ADO.NET application. He can get the password from the .exe file, from a memory dump, by packet sniffing it from the network, by spoofing the server information, and so forth.

A good solution is to use integrated security. This implies that your program can do anything the authenticated user has the permissions to do without your program. With integrated security, no passwords will appear in the process.

As an example of the dangers of using passwords in connections strings, take this scenario. First, you call SqlConnection.Open with a connection string containing a password. Second, SqlConnection encrypts the password internally as soon as possible. However, even with this design, a memory dump of the process before a garbage collection occurs will show you the string object (connection string) that you used when calling SqlConnection.Open. So no matter what SqlConnection does, it cannot hide the contents of this connection string. SqlConnection cannot even erase the data in the incoming connection string, as strings are immutable in .NET. For more info on securing strings in .NET-based apps, see Security Briefs in this issue of MSDN Magazine, specifically the section on System.Security and the new SecureString class.

A second option is to completely hide the database from the user (using a Web service solution, for example) so that the database can only be accessed by the Web server.

Q I would like to know how to prevent the menu items in my page from appearing behind the map I'm displaying. The map is a flash object held inside a <DIV> tag. Should I use a z-index? When I move the mouse over the main menu, the menu items should appear, but some of them are hidden behind the map.

A Setting the z-index attribute on your <DIV> element won't help because the Flash control is a windowed element and will not allow other DHTML content to be layered on top of it. You may be able to change your menu to use createPopup instead, which can then sit on top of another windowed object. However, be careful that accessibility features will still be able to use the menu options. For more information on how to do this, see createPopup Method.

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

Thanks to the following Microsoft developers for their technical expertise: Paul Bates, Todd Carter, Blaine Dockter, Kirk Evans, Simon Hoare, Philo Janus, Jon Langdon, Eric Lawrence, Drew Leaumont, Mauricio Lorenzon, Dave Massy, Joseph Minckler, Matt Neerincx, Philip Reilly, Markus Rheker, Angel Saenz-Badillos, Bill Safcik, Sanjeeb Kumar Sarangi, Doug Shattuck, Chad Sheffield, Nuno Silva, Rob Smith, and Kevin Yu.


Advanced Basics: Creating a Five-Star Rating Control

Advanced Basics
Creating a Five-Star Rating Control
Duncan Mackenzie

Code download available at: AdvancedBasics0501.exe (144KB)
Contents
Getting Started
Property Routines with Special Default Values
Drawing Out Custom and Standard Images
Handling and Raising Events
Designing for Inheritance
Code Organization
Accessibility
Ease-of-Use
The Triangles Class, an Inheritance Example
Conclusion

I have to admit it; most of my Windows® Forms controls are an attempt to copy something that already exists. In my October 2004 column I showed you how to create a progress bar that mimicked the one shown during the Windows XP setup routine, and this month I'm at it again. This time, the object of my desire is the cool five-star rating control in Windows Media® Player (see Figure 1).

Figure 1 The Five-Star Rating Control
Figure 1 The Five-Star Rating Control

This control looks great and provides a nice visual way to view ratings, but it is the editing experience that I find especially cool. When the cursor hovers over this column, Windows Media Player highlights the stars to indicate the value you are currently floating over, providing a nice bit of visual feedback. This same type of user interface is found on various Web sites, including Netflix and Amazon, and I'd like to have the functionality in my own applications so I decided to create my own. I'll use a Windows Forms control to emulate this user interface element, while trying to make it customizable enough for use in a variety of situations.

Getting Started

The first step is to create a new Class Library project to hold the control and an empty Windows application to be my test project. The Windows Control Library project template may seem more appropriate, and it will work just fine, but by default that project includes User Controls (which are generally used for composite controls—Windows Forms controls that contain one or more other controls), and all I need is an empty Class file. Next, you must make your currently empty new Class inherit from System.Windows.Forms.Control, which is easily accomplished by adding a single line after the class declaration:

Public Class Ratings
    Inherits System.Windows.Forms.Control

End Class

If you try to add the Inherits statement using only IntelliSense® you will notice a small problem: starting your project with a Class Library template doesn't add a reference to the System.Windows.Forms assembly, so you need to add it manually. At this point, I'd go ahead and also add a reference to the System.Drawing.dll, because a custom-drawn control will need to use it eventually.

From this point on, I generally follow these steps for all my control development:

  1. Add a standard constructor for any custom-drawn control, setting up all the control styles required for the control to draw correctly and as smoothly as possible.
    Public Sub New()
    Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
    Me.SetStyle(ControlStyles.DoubleBuffer, True)
    Me.SetStyle(ControlStyles.ResizeRedraw, True)
    Me.SetStyle(ControlStyles.UserPaint, True)
    ... 'add any additional initialization code here
End Sub
  2. Working on paper, figure out the list of public properties you would need to configure the control's behavior and appearance.
  3. Add all those properties as private member variables (I like using Hungarian notation to indicate that they are internal variables by prefixing each with "m_"), and including default values where appropriate, as shown in Figure 2.
  4. Turn those into property procedures, most of which are pretty straightforward (get value, set value), but a few of which will require some additional code that I'll cover in a little while.
  5. Start planning out and writing the custom drawing code.
  6. Finally, I add any new events, like click handling or whatever special events are needed for this particular control.

Figure 2 Control Member Variables (Close Figure 2)
Private m_FilledImage As Image
Private m_EmptyImage As Image
Private m_HoverImage As Image
Private m_ImageCount As Integer = 5
Private m_TopMargin As Integer = 2
Private m_LeftMargin As Integer = 4
Private m_BottomMargin As Integer = 2
Private m_RightMargin As Integer = 4
Private m_ImageSpacing As Integer = 8
Private m_ImageToDraw As Integer = 1

Private m_SelectedColor As Color = Color.Empty
Private m_HoverColor As Color = Color.Empty
Private m_EmptyColor As Color = Color.Empty
Private m_OutlineColor As Color = Color.Empty

Private m_selectedItem As Integer = 3
Private m_hoverItem As Integer = 1
Private m_hovering As Boolean = False

Private ItemAreas() As Rectangle

Property Routines with Special Default Values

I want a few of my properties—those dealing with colors—to default to values that reflect other properties on the control (such as ForeColor), and others that reflect the user's system colors. Taking just one of these colors, HoverColor, as an example, let's take a look at the different ways I could produce a default value.

The first way is the most obvious, simply set the default value in the variable declaration (or in the constructor):

Private m_HoverColor As Color = _
    Color.FromKnownColor(KnownColor.Highlight)

This will work fine in most cases, but it has two problems. First, what if the user changes their system colors while the application is running? The control will reflect the correct colors after restarting the program, but not until then. Second, what if the user wants to programmatically set the color back to the default? There is no real way to clear the color setting and have it use the appropriate system color. The user could certainly set it to the appropriate system color directly, but then you'd be back to the first problem again.

Another option is to trap when the user's system colors change, and change your property values accordingly:

Protected Overrides Sub OnSystemColorsChanged( _
        ByVal e As System.EventArgs)
    Me.HoverColor = Color.FromKnownColor(KnownColor.Highlight)
    Me.Invalidate()
End Sub

This solution doesn't really work, unless you have some way to know whether or not the property is set to the default or if it was set to a specific color by the developer using the control. The overhead of tracking that information would probably not be worth the effort. As an alternative, I decided to use a null/empty default value and then return the appropriate default in the property routine itself, as shown in Figure 3.

Figure 3 Default Property Values (Close Figure 3)
Public Property HoverColor() As Color
    Get
        If m_HoverColor.Equals(Color.Empty) Then
            Return Color.FromKnownColor(KnownColor.Highlight)
        Else
            Return m_HoverColor
        End If
    End Get
    Set(ByVal Value As Color)
        If Not Value.Equals(m_HoverColor) Then
            m_HoverColor = Value
            Me.Invalidate()
        End If
    End Set
End Property

This addresses the issues I have raised so far, including the handling of changes to the system colors, knowing when it is supposed to return the default versus when the user has set it, and allowing the user to reset the value to the default value (when myControl.HoverControl = Color.Empty).

Drawing Out Custom and Standard Images

In the control I'm building, I decided to allow two main categories of images: standard and user-supplied. The initial control only supports two standard images (circles and squares), but later I'll discuss one way to add custom images to this list.

All of the drawing for this control is handled in the OnPaint routine, which I overrode to provide my own rendering code (see Figure 4). Within that routine, I calculate the position for each image (using the ImageCount property to determine the number of images that should be drawn) and then call either DrawStandardImage (to draw a circle or a square) or DrawUserSuppliedImage (to draw images that the user provided).

Figure 4 Rendering the Control (Close Figure 4)
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    e.Graphics.Clear(Me.BackColor)
    Dim imageWidth, imageHeight As Integer
    imageWidth = (Me.Width-(LeftMargin + RightMargin + ( _
        Me.m_ImageSpacing * (Me.m_ImageCount-1)))) \ Me.m_ImageCount
    imageHeight = (Me.Height-(TopMargin + BottomMargin))

    Dim start As New Point(Me.LeftMargin, Me.TopMargin)

    For i As Integer = 0 To Me.ImageCount-1
        Me.ItemAreas(i).X = start.X-Me.ImageSpacing \ 2
        Me.ItemAreas(i).Y = start.Y
        Me.ItemAreas(i).Width = imageWidth + Me.ImageSpacing \ 2
        Me.ItemAreas(i).Height = imageHeight

        If Me.ImageToDraw = UserSuppliedImage Then
            DrawUserSuppliedImage(e.Graphics, _
                start.X, start.Y, imageWidth, imageHeight, i)
        Else
            DrawStandardImage(e.Graphics, Me.ImageToDraw, _
                start.X, start.Y, imageWidth, imageHeight, i)
        End If
        start.X += imageWidth + Me.ImageSpacing
    Next
    MyBase.OnPaint(e)
End Sub

Protected Overridable Sub DrawUserSuppliedImage( _
        ByVal g As Graphics, _
        ByVal x As Integer, ByVal y As Integer, _
        ByVal w As Integer, ByVal h As Integer, _
        ByVal currentPos As Integer)

    Dim img As Image
    If m_hovering And m_hoverItem > currentPos Then
        img = Me.HoverImage
    ElseIf Not m_hovering And m_selectedItem > currentPos Then
        img = Me.FilledImage
    Else
        img = Me.EmptyImage
    End If

    If Not img Is Nothing Then
        g.DrawImage(img, x, y, w, h)
    Else
        Me.DrawStandardImage(g, Me.Circle, x, y, w, h, currentPos)
    End If
End Sub

Protected Overridable Sub DrawStandardImage( _
        ByVal g As Graphics, ByVal ImageType As Integer, _
        ByVal x As Integer, ByVal y As Integer, _
        ByVal w As Integer, ByVal h As Integer, _
        ByVal currentPos As Integer)

    Dim fillBrush As Brush
    Dim outlinePen As Pen = New Pen(Me.OutlineColor, 1)

    If m_hovering And m_hoverItem > currentPos Then
        fillBrush = New SolidBrush(Me.HoverColor)
    ElseIf Not m_hovering And m_selectedItem > currentPos Then
        fillBrush = New SolidBrush(Me.SelectedColor)
    Else
        fillBrush = New SolidBrush(Me.EmptyColor)
    End If

    Select Case ImageType
        Case Me.Square
            g.FillRectangle(fillBrush, x, y, w, h)
            g.DrawRectangle(outlinePen, x, y, w, h)
        Case Me.Circle
            g.FillEllipse(fillBrush, x, y, w, h)
            g.DrawEllipse(outlinePen, x, y, w, h)
    End Select
End Sub

These routines are not the most efficient (I always redraw the entire control, for example, rather than invalidating only those regions affected by the specific update), but they take care of drawing the appropriate images (or appropriately colored images, in the case of the standard options) whenever necessary. Throughout the rest of the control's code, whenever a property or state change occurs that would result in a change in the control's appearance, a complete redraw is triggered by calling Me.Invalidate. The override routine for OnMouseMove is an example of this type of code:

Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
    For i As Integer = 0 To Me.ImageCount-1
        If Me.ItemAreas(i).Contains(e.X, e.Y) Then
            Me.m_hoverItem = i + 1
            Me.Invalidate()
            Exit For
        End If
    Next
    MyBase.OnMouseMove(e)
End Sub

Handling and Raising Events

At this point, the control is functional, mostly due to all of the wonderful functionality obtained by inheriting from System.Windows.Forms.Control. This inheritance relationship gives our control a bunch of free features, including a Click event and the ability to be dragged onto the design surface of a Windows Form. More than just those standard features are needed though, so I'll add a new Event and code into a few key areas (see Figure 5).

Figure 5 SelectedItem and SelectedItemChanged (Close Figure 5)
Public Event SelectedItemChanged As EventHandler

Protected Overridable Sub OnSelectedItemChanged()
    RaiseEvent SelectedItemChanged(Me, EventArgs.Empty)
End Sub

Public Property SelectedItem() As Integer
    Get
        Return m_selectedItem
    End Get
    Set(ByVal Value As Integer)
        If Value >= 0 And Value <= Me.ImageCount + 1 Then
            m_selectedItem = Value
            OnSelectedItemChanged()
        Else
            Value = 0
        End If
    End Set
End Property

This new Event, SelectedItemChanged, is more than just a convenience; it also has the nice side effect of improving data binding performance. If the Windows Forms data binding code sees an event with a name following the pattern of <bound property name>Changed, and with a signature defined as System.EventHandler, then it will use that event as notification of a change to the bound property. Monitoring this event is much less work than polling the property for any changes, so the end result is more efficient data binding.

The only other routines I need to add to my control are overrides for the OnMouseEnter and OnMouseLeave routines to ensure that I am correctly displaying the control when the user hovers over it. As shown in Figure 6, I also need to override the OnClick routine so that I can correctly update the currently selected item when the user picks a new rating value.

Figure 6 OnClick Override (Close Figure 6)
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
    Dim pt As Point = Me.PointToClient(Me.MousePosition)

    For i As Integer = 0 To Me.ImageCount-1
        If Me.ItemAreas(i).Contains(pt) Then
            Me.m_hoverItem = i + 1
            Me.SelectedItem = i + 1
            Me.Invalidate()
            Exit For
        End If
    Next
    MyBase.OnClick(e)
End Sub

At this point, there is a lot of "lipstick" I could add, such as attributes to specify a toolbox bitmap and categorize my properties, but the control is basically finished and works fine. The next trick, though, would be to allow another developer to extend my work to support additional shapes.

Designing for Inheritance

Inheritance only works when classes are designed to be inherited from. Okay, so maybe that is a bit of a strong statement. Inheritance will always work (as long as the class you are inheriting from is not marked as NotInheritable), but when the possibility of future inheritance is considered during the design of a base class, it is generally easier for others to add the functionality they want to add later. To design a class that will be easily inherited from, the first step is to determine how other developers will want to extend it. You can't predict everything another developer might want to do, but you can certainly surmise what some of the most obvious modifications might be. With that in mind, you can now look at your code for organizational issues, accessibility, and ease of use.

Note whether your code is broken up into functions that best encapsulate the areas in which someone might want to extend the class, or whether others would have to rewrite a lot of unrelated code just to add something new. Also note whether you've set the access modifiers (Public, Private, Protected) on your variables and routines appropriately. Remember that your goal is to make the end user's experience as seamless as possible.

There are other concerns when designing your classes to participate in inheritance, but these are the ones I thought about when looking at this particular sample. Taking them in order, I'll discuss the changes I made to my "base" class (Ratings) in order to make it easier to extend.

Code Organization

In order to organize my code most efficiently (although I probably would have done this for clarity anyway), I didn't put the image and shape drawing code right into OnPaint. By leaving them as their own routines, a developer can override one of those routines without having to worry about all the item positioning and graphics setup that occurs in OnPaint. I also was careful to mark those two drawing routines (along with the majority of my routines in this class) as Overridable because they seemed like a likely target for extension.

Accessibility

To make my code somewhat accessible, I made my two drawing routines Protected, instead of Private, enabling them to be used by classes inheriting from my class, but still hiding them from the public interface. I also marked several additional routines, including OnSelectedItemChanged, as Protected, enabling a child class to call those routines if it proves necessary.

Ease-of-Use

The fuzziest of my three points, ease-of-use, is about making an extended version of your class as easy to use as the base class. Of course, you don't control what the inheriting developer will do with your class, so you can't guarantee that it will be easy to use in the end, but you can try to improve the odds. In the case of my class, I had originally created the ImageType property as an Enum (with UserDefined, Square, and Circle included), which led to code like the following:

sr.ImageToDraw = ImageType.Circle

When I try to imagine this in an inheritance situation where the child class has added a new ImageType, there's a problem. The enum can't be extended, so you get this:

sr.ImageToDraw = 3 'some number not in our original enum

This would be a problem in terms of strong typing (although you can make it work because Enums are generally Int32 types under the covers), and it just isn't as pretty. To fix this problem, I ditched the Enum and defined my ImageTypes as public constants on my control class, making the code look like this:

sr.ImageToDraw = Ratings.Circle
And in the case of the child class with the new shape type, like this:
sr.ImageToDraw = myNewClass.NewShape

The Triangles Class, an Inheritance Example

Because of the work I have done so far, I was able to inherit from my control and extend it with a new image type with just a few minutes worth of coding. Figure 7 shows the end result—a class that supports a single new shape type (triangle).

Figure 7 MTS
Figure 7 MTS

The only code I needed was the override of the DrawStandardImage and a new constant, as shown in Figure 8.

Figure 8 Adding a New Ratings Shape (Close Figure 8)
Public Class Triangles
    Inherits Ratings

    Public Const Triangle = 3

    Protected Overrides Sub DrawStandardImage( _
            ByVal g As System.Drawing.Graphics, _
            ByVal ImageType As Integer, _
            ByVal x As Integer, ByVal y As Integer, _
            ByVal w As Integer, ByVal h As Integer, _
            ByVal currentPos As Integer)

        Select Case ImageType
            Case Triangles.Triangle
                Dim fillBrush As Brush
                Dim outlinePen As Pen = New Pen(Me.OutlineColor, 1)

                If IsHovering AndAlso HoverItem > currentPos Then
                    fillBrush = New SolidBrush(Me.HoverColor)
                ElseIf Not IsHovering AndAlso _
                        SelectedItem > currentPos Then
                    fillBrush = New SolidBrush(Me.SelectedColor)
                Else
                    fillBrush = New SolidBrush(Me.EmptyColor)
                End If

                Dim pts(2) As PointF

                pts(0).X = (x + (w / 2))
                pts(0).Y = y
                pts(1).X = x + w
                pts(1).Y = y + h
                pts(2).X = x
                pts(2).Y = y + h

                g.FillPolygon(fillBrush, pts)
                g.DrawPolygon(outlinePen, pts)

            Case Else
                MyBase.DrawStandardImage(g, ImageType, _
                    x, y, w, h, currentPos)
        End Select
    End Sub
End Class

Control development, whether for Windows or the Web, is a great way to make reusable pieces of code, but if you want to allow other developers to build upon your work then you need to plan carefully for inheritance.

Conclusion

The finished product this month is a simple ratings control, but it would be truly useful if you could use it as a column inside a DataGrid. In my next installment of Advanced Basics, I'll take you through the process of writing your own custom control for use as a DataGrid column, using this rating control as the example. I'll see you then.

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

Duncan Mackenzie is the MSDN Content Strategist for Visual Basic and C# and the author of the Coding 4 Fun column on MSDN online. Duncan can be reached through his blog by visiting weblogs.asp.net/duncanma or at his personal site, www.duncanmackenzie.net.


Data Points: Data Source Controls in ASP.NET 2.0

Data Points
Data Source Controls in ASP.NET 2.0
John Papa

Code download available at: DataPoints0501.exe (164KB)
Contents
Data-Bound Controls
Data Source Controls
Command Types and Parameters
SqlDataSource Example
ObjectDataSource
Enhanced Strongly Typed DataSet
More Data Source Control Trivia
Wrapping Up

ASP.NET 2.0 introduces a series of new tools that improve data access including several data source and data bound controls. The new assortment of data source controls can eliminate a ton of repetitive code that was required in ASP.NET 1.x. For example, you can easily associate SQL statements or stored procedures with data source controls and bind them to data bound controls. Even more impressive, the ObjectDataSource control allows you to take advantage of the simplified development and reduction of code but still allows you to abstract your business and data access logic in separate tiers of your n-tiered architecture.

Before .NET, building data grids with traditional ASP often required you to write a lot of code to build an HTML table on the fly while looping through an ADO Recordset. ASP.NET 1.x made this type of development easier by allowing you to bind an XML-based DataSet to the ASP.NET DataGrid control. This reduced the code required to generate the grid. However, both traditional ASP and ASP.NET 1.x require code to implement paging, sorting, editing and row-selection features. With the improvements in ASP.NET 2.0, this code can be significantly reduced to produce a data-filled grid with full paging, sorting, and editing features.

In this installment of Data Points, I will begin by demonstrating how easy it is to develop a Web application with ASP.NET 2.0 using the SqlDataSource and some of the new data-bound controls. Note that I have used the Beta 1 release here.

Most enterprise applications are built on multitiered architectures with a middle tier that holds the business logic and a data access layer that works with the backend database (or databases). I will discuss how the ObjectDataSource is ideal for integrating with existing multitier components. By linking the ObjectDataSource control to business objects, you are able to leverage an existing multitiered architecture and take advantage of the significant reduction in code to generate a sophisticated Web UI. The ObjectDataSource control also contains some special properties that allow binding to the newly enhanced strongly typed DataSet and data components in ASP.NET 2.0 and ADO.NET 2.0. Other new features and improvements in ASP.NET 2.0 include the new two-way binding expressions, enhanced caching, and several new ASP.NET 2.0 controls that can be data bound to the new data source controls.

Data-Bound Controls

To use the data source controls you must have a data-bound control to bind them to. There are several new data-bound controls in ASP.NET 2.0, including the GridView, DetailsView and FormView controls. If you are fond of the ASP.NET 1.x DataGrid control, you will love the ASP.NET 2.0 GridView control. The GridView is like a DataGrid on steroids as it can be bound to the new data source controls, and can be used to implement sorting, editing, and paging, all with much less code than the DataGrid required (for more information on the GridView, refer to Dino Esposito's article from the August 2004 issue of MSDN®Magazine,).

To bind a GridView to a data source control, you set the GridView's DataSourceID property to the ID of the data source control. There are several other properties of the GridView that can be set to enhance appearance and user interaction that I will demonstrate in the later examples:

<asp:GridView ID="gvwOrders" Runat="server"
    DataSourceID="sdsOrdersDataSource"
    AutoGenerateColumns="True">

Other controls, such as the DropDownList, can also be bound to the data source controls. For example, a DropDownList control can be bound to a SqlDataSource control that retrieves a list of employees. The employee's full name could be displayed in the DropDownList while the EmployeeID could be bound to the control as its underlying data value field. The following example defines a DropDownList that will display a list of customer names that can be selected. The customer data is bound to the SqlDataSource control named sdsCustomerDataSource which gets a list of customer CompanyName and CustomerID fields:

<asp:DropDownList ID="ddlCustomers" Runat="server" AutoPostBack="True"
    DataSourceID="sdsCustomersDataSource"
    DataTextField="CompanyName"
    DataValueField="CustomerID">
</asp:DropDownList>

Binding a control to a data source control is quite simple in ASP.NET 2.0 and does not require any code in a codebehind. However, you can still write code to explicitly bind to the controls if you want to. In fact, the data source and DataMember proper-ties of data-bound controls have been brought forward from ASP.NET 1.x, as well.

Data Source Controls

There are several new data source controls in ASP.NET 2.0, such as the SqlDataSource, ObjectDataSource, XmlDataSource, AccessDataSource, and SiteMapDataSource (shown in Figure 1). They all can be used to retrieve data from their respective types of data sources and can be bound to various data-bound controls. Data source controls simplify the amount of custom code that needs to be written to retrieve and bind data, and even to sort, page through, or edit data.

Figure 1 ASP.NET 2.0 Data Source Controls (Close Figure 1)

Data Source Control | Retrieves Its Data From
SqlDataSource | A SQL Server, Oracle, or other OLE DB accessible database
AccessDataSource | An access database
ObjectDataSource | A business object or method
XmlDataSource | An XML file or stream (used for hierarchical data)
SiteMapDataSource | An XML-based sitemap source (used for hierarchical data)

Each data source control has similar properties that allow it to interact with its respective data source. The SiteMapDataSource and the XmlDataSource are built to retrieve hierarchical data while the other data source controls are a built to retrieve set-based data with columns and rows.

The AccessDataSource is built specifically to retrieve data from Access databases. The SqlDataSource may sound like it works only with SQL Server™, but that's not the case. It can actually be used to retrieve data from any OLE DB or ODBC-compliant data source.

Command Types and Parameters

The SqlDataSource control has four command properties that can be set to tell the SqlDataSource how to get, insert, update, and delete its data. The SelectCommand property can be set to a SQL statement or a stored procedure. In either case, parameters can be passed in if needed. The InsertCommand, UpdateCommand, and DeleteCommand properties are used to tell the SqlDataSource what SQL statements (or stored procedures) to use to modify the data in the underlying database. The code example in Figure 2 shows a SqlDataSource that has its SelectCommand and UpdateCommand properties set to parameterized SQL statements. Notice that UpdateParameters uses a Parameter element to indicate the names and data types of the fields to use for the parameters. Once a GridView is bound to this SqlDataSource, these UpdateParameters values are bound to affected row's columns with the same names.

Figure 2 SqlDataSource Control (Close Figure 2)
<asp:SqlDataSource ID="sdsCustomersDataSource" Runat="server"
    ProviderName="System.Data.SqlClient"
    ConnectionString="Server=(local);Integrated
        Security=True;Database=Northwind"
    SelectCommand="SELECT CustomerID, CompanyName FROM Customers"
    UpdateCommand="UPDATE Customers SET CompanyName = @CompanyName WHERE
        CustomerID = @CustomerID">
    <SelectParameters>
        <asp:ControlParameter Name="CustomerID" Type="String"
            ControlID="ddlCustomers"></asp:ControlParameter>
     </SelectParameters>
     <UpdateParameters>
         <asp:Parameter Name="CompanyName" Type="String"></asp:Parameter>
         <asp:Parameter Name="CustomerID" Type="String"></asp:Parameter>
     </UpdateParameters>
</asp:SqlDataSource>

The easiest way to try this out for yourself is to create a Web Form in Visual Studio® 2005, connect to the local SQL Server Northwind database in the Server Explorer window, and drag a table to the Web Form. This will automatically create a SqlDataSource control as well as a GridView. Visual Studio will automatically set the SqlDataSource control's ProviderName and ConnectionString properties to the SQL Server Northwind database. Also, all four of the command properties will be set to the appropriate SQL statements. Then, all you have to do to edit the Web Form is use the Smart Tag to check the Enable Editing checkbox (see Figure 3).

Figure 3 GridView Settings
Figure 3 GridView Settings

The data source controls can use parameters from other controls, too. For example, a data source control may retrieve all orders for a given customer. In this case, the CustomerID could be a parameter that is passed to a SQL statement or a stored procedure of a SqlDataSource control's SelectCommand property. The CustomerID could be retrieved from another control, such as a DropDownList, and passed directly into the SQL SelectCommand of the SqlDataSource control.

You can link a control's value directly to a parameter of any of the SqlDataSource control's SQL statements (SelectCommand, InsertCommand, UpdateCommand, or DeleteCommand). You can also specify exactly which property of a control you want to use for the parameter. For example, if you did not want the default property of the DropDownList in the previous example, but instead wanted its DataTextField, you could set the ControlParameter's PropertyName property to DataTextField.

Besides the ControlParameter, there are other parameter types that can be used with data source controls. A ProfileParameter can be used to retrieve a parameter's value from a profile object if you're using the new ASP.NET personalization features. Then there are several parameter object types that all retrieve their data from the standard Request object's collections. For example, a CookieParameter can be used to retrieve a parameter's value from a cookie. The QueryStringParameter gets its value from any request string variable and the FormParameter gets its value from an HTML Form's input field. Finally, the SessionParameter can be used to retrieve its value from a session variable. These types of parameters give data source controls several options on how to set their parameters' values.

SqlDataSource Example

Now that I've finished the overview, I will compare and contrast the retrieval and modification of data using both the SqlDataSource and the ObjectDataSource. The SqlDataSource control, which uses the ADO.NET 2.0 DbProviderFactory object, has properties that link it directly to an OLE DB or ODBC data source. When loading an ASP.NET page that contains a data-bound control linked to a SqlDataSource, the SqlDataSource communicates directly with an underlying database. Thus, the SqlDataSourceData source control does not integrate with existing business objects.

To see the SqlDataSource in action, let's take a look at the Orders_SDS.aspx page (see Figure 4). There is a DropDownList control that is bound to a SqlDataSource source control the retrieves a list of customers from the Northwind database.

Figure 4 Retrieving Customers Through a SqlDataSource
Figure 4 Retrieving Customers Through a SqlDataSource

There is also another SqlDataSource called sdsOrdersDataSource which retrieves all Orders for the selected customer. The code in Figure 5, taken from Orders_SDS.aspx (which is available in the download), shows the two SqlDataSource controls and the DropDownList. Notice that the sdsOrdersDataSource SqlDataSource control uses the selected value of the DropDownList for its stored procedure's parameter (prGet_Orders's parameter).

Figure 5 SqlDataSource Controls (Close Figure 5)
<asp:SqlDataSource ID="sdsCustomersDataSource" Runat="server"
    ProviderName="System.Data.SqlClient"
    ConnectionString="Server=(local);Integrated Security=True;
        Database=Northwind"
    SelectCommand="prGet_Customers">
</asp:SqlDataSource>

<asp:SqlDataSource ID="sdsOrdersDataSource" Runat="server"
    ProviderName="System.Data.SqlClient"
    ConnectionString="Server=(local);Integrated Security=True;
        Database=Northwind"
    SelectCommand="prGet_Orders" UpdateCommand="prUpdate_Order">
    <UpdateParameters>
        <asp:Parameter Type="DateTime" Name="OrderDate"></asp:Parameter>
        <asp:Parameter Type="String" Name="ShipCity"></asp:Parameter>
        <asp:Parameter Type="String" Name="ShipCountry"></asp:Parameter>
        <asp:Parameter Type="Int32" Name="OrderID"></asp:Parameter>
    </UpdateParameters>
    <SelectParameters>
        <asp:ControlParameter Name="CustomerID" Type="String"
             ControlID="ddlCustomers"></asp:ControlParameter>
    </SelectParameters>
</asp:SqlDataSource>

<asp:DropDownList ID="ddlCustomers" Runat="server"
    DataSourceID="sdsCustomersDataSource"
    DataTextField="CompanyName" DataValueField="CustomerID"
    AutoPostBack="True">
</asp:DropDownList>

The combination of the ProviderName and the ConnectionString properties tell the SqlDataSource controls which data store to get the data from. While these properties make the SqlDataSource a simple solution for retrieving and modifying data, they unfortunately expose database connection strings and SQL statements or stored procedures in the presentation layer's ASPX files. This is generally not a good idea. It's much safer to store this data encrypted in a configuration repository (like a configuration file or the registry).

Figure 4 shows the Orders_SDS.aspx page in edit mode. Paging is automatically implemented by the GridView by setting the GridView AllowPaging property to true and the PageSize property to a desired size, like 10. This tells the grid to page the rows in the grid and automatically reload the grid and the page when the next or previous page links are clicked. The GridView displays the selected row's editable columns using TextBox controls. The data-bound columns are defined with either the asp:BoundField or TemplateField elements. The BoundField can be bound to the GridView's associated data source by setting the DataField property:

<asp:BoundField HeaderText="ShipCity" DataField="ShipCity"
    SortExpression="ShipCity"></asp:BoundField>

This tells the GridView control that it is to display the ShipCity value in a span element when it is in view mode. When the GridView is in edit mode, this column in the selected row will be displayed in the appropriate HTML element. In this case, the HTML element is the TextBox because it is a string value. The element is chosen based on the data type of the bound column. For example, if the column is defined as a bit in SQL Server, then the CheckBox control would be used to display the column in edit mode.

The TemplateField element offers greater flexibility on how the bound column should behave. Take a look at the following code sample from the Orders_SDS.aspx page:

<asp:TemplateField SortExpression="OrderDate" HeaderText="OrderDate">
    <ItemTemplate>
        <asp:Label ID="lblOrderDate_Item" Runat="server"
            Text='<%# Bind("OrderDate", "{0:d}") %>'></asp:Label>
    </ItemTemplate>
    <EditItemTemplate>
        <asp:TextBox ID="txtOrderDate_Edit" Runat="server"
            Text='<%# Bind("OrderDate", "{0:d}") %>'></asp:TextBox>
    </EditItemTemplate>
</asp:TemplateField>
It uses a TemplateColumn to display the OrderDate column in a Label control when the GridView is in view mode and in a TextBox control when it is in edit mode. You can also tell the Template column to display this column differently in the footer by using a FooterTemplate. There is also a HeaderTemplate, an AlternatingItemTemplate, and even an InsertItemTemplate. Also notice the shortened binding syntax introduced in ASP.NET 2.0. The value for the OrderDate is set by calling the Bind expression and passing to it the name of the column in the GridView's associated data source and an optional data format string expression. In this case, I used the data format string that represents a short date. These properties are all easily set through the properties accessible via the new Smart Tag feature in Visual Studio 2005 (see Figure 6).

Figure 6 Template Column Properties
Figure 6 Template Column Properties

ObjectDataSource

One of the coolest features of the GridView and the other data-bound controls is that once they are set up, they can be bound to either an ObjectDataSource control or a SqlDataSource control by simply changing a single property. For example, all you have to do is create a new ObjectDataSource control and change the DataSourceID property of the GridView to this new ObjectDataSource control's ID.

Unlike the SqlDataSource, the ObjectDataSource control lets you abstract the database-specific settings out of the ASPX page and the presentation layer and move them to a lower tier in a multitier architecture (see Figure 7). For example, the ConnectionString, ProviderName, and SelectCommand properties of the SqlDataSource control do not exist in the ObjectDataSource control. Instead, they are replaced with other properties which tell the ObjectDataSource control which business class to instantiate and which method to use to retrieve or modify the data.

Figure 7 Tiers
Figure 7 Tiers

To set up an ObjectDataSource control to access another tier's business class and its methods, you must first set the ObjectDataSource control's TypeName property to the name of the business class (like TypeName="MSDN2005Jan_BLL.Orders"). Follow that by setting the SelectMethod property to the name of the method in the business class that will be used to retrieve the data for the data source. The business class's method must return an enumerable list such as a collection, array, DataSet, or DataReader. In order for this to work, the ObjectDataSource must be able to execute the specified method. If it's a static method, nothing special needs to be done. If the method is an instance method, the ObjectDataSource must be able to create an instance of the class. The easiest way for this to happen is to write the business class to include a default constructor. Alternatively, you can handle the ObjectDataSource's ObjectCreating event, which allows you to instantiate the object with whatever constructor you desire and then pass that object instance to the data source control.

The code sample in Figure 8 (excerpted from Orders.aspx, also in the download) shows two ObjectDataSource controls that replace the two SqlDataSouce controls used in the previous example. The odsOrdersDataSource uses the MSDN2005Jan_BLL.Orders class and its GetData method to retrieve its list of orders. In this example, the GetData method in the Orders class simply creates an instance of a strongly typed DataSet named OrdersDataSet and its related adapter called OrdersTableAdapter. It then calls the Fill method and returns the strongly typed DataSet:

public OrdersDataSet GetData(string CustomerID)
{
    OrdersDataSet oDs = new OrdersDataSet();
    OrdersTableAdapter oDa = new OrdersTableAdapter();
    oDa.Fill(oDs, CustomerID);
    return oDs;
}

Figure 8 Defining the ObjectDataSource Controls (Close Figure 8)
<asp:ObjectDataSource ID="odsCustomersDataSource" Runat="server"
    TypeName="MSDN2005Jan_BLL.Customers"
    SelectMethod="GetData" EnableCaching="True" CacheDuration="1200">
</asp:ObjectDataSource>

<asp:ObjectDataSource ID="odsOrdersDataSource" Runat="server"
    TypeName="MSDN2005Jan_BLL.Orders"
    SelectMethod="GetData" UpdateMethod="UpdateData">
<UpdateParameters>
    <asp:Parameter Type="Int32" Name="OrderID"></asp:Parameter>
    <asp:Parameter Type="DateTime" Name="OrderDate"></asp:Parameter>
    <asp:Parameter Type="String" Name="ShipCity"></asp:Parameter>
    <asp:Parameter Type="String" Name="ShipCountry"></asp:Parameter>
</UpdateParameters>
<SelectParameters>
    <asp:ControlParameter Name="CustomerID" Type="String"
        ControlID="ddlCustomers"
        PropertyName="SelectedValue"></asp:ControlParameter>
    </SelectParameters>
</asp:ObjectDataSource>

Notice that the GetData method also accepts a CustomerID parameter passed in by the ObjectDataSource control's SelectParameter properties. The UpdateData method's parameters are also passed in from the ObjectDataSource as indicated by the UpdateParameters properties shown in Figure 8. The following is the signature of the Orders class's UpdateData method:

public void UpdateData(int OrderID, DateTime OrderDate,
    string ShipCity, string ShipCountry)

The names and data types of the UpdateData method's parameters must match the names and data types of update parameters in the ObjectDataSource control. The data source controls don't work with batch updates, so you can't pass multiple rows to the update method all in one shot. Instead, you must pass each value as a single parameter to the update method. In addition to the SelectMethod and UpdateMethod properties, the ObjectDataSource control also has DeleteMethod and InsertMethod properties, too.

Enhanced Strongly Typed DataSet

The previous example demonstrated how to bind a GridView to an ObjectDataSource control to link to a business tier's class so you can retrieve and update data via the class's methods. This example works great if you have existing business layer logic and a multitiered architecture. It can also call methods of a Web service client proxy or any other referenced class that follows the class and method requirements.

One aspect of the previous example I intentionally delayed discussing until now is the role of the strongly typed DataSet. Using the wizards in Visual Studio 2005 you can also define methods right in the typed DataSet class to select, insert, update, and delete data. Thus, you can avoid directly writing any ADO.NET code in the business or data access layer and instead use the wizards to add the ADO.NET logic right into a typed DataSet.

You are not required to retrieve a typed DataSet, but one would be valuable in this situation, thanks to some of their new enhancements. The typed DataSet creates a default Fill method, which is appended to a TableAdapter class in the typed DataSet's definition. This optional TableAdapter class can be set up to store the connection string as well as the stored procedures or SQL statements to select, update, insert, and delete records from a database.

Figure 9 Orders DataSet
Figure 9 Orders DataSet

The typed DataSet also allows you to create custom methods to retrieve and modify data. In the Orders DataSet, shown in Figure 9, I created a typed DataSet based on the prGet_Orders stored procedure. Then, using the data component query configuration wizard, I added two custom methods to the OrdersTableAdapter: GetData and UpdateData. These methods are defined within the class file associated with the typed DataSet's XSD; in this case my file is called OrdersDataSet.Designer.cs. If you are curious, you can open this auto-generated file (but you should not modify it since your changes will be overwritten if the file is regenerated) and review the custom GetData and Update methods as well as all of the standard code created for a typed DataSet. If you run the sample page Orders2.aspx, it binds directly to these custom methods. This can dramatically reduce the amount of code you manually write for your middle tiers.

More Data Source Control Trivia

Now that we're finished, you might be interested to know that the data source controls also expose caching functionality through a series of properties. By setting EnableCaching to true and setting the CacheDuration to a number of seconds, the data will be stored in cache for that period of time. The CacheExpirationPolicy property can be set to Absolute or Sliding, as well. Absolute is the default, which tells cache to begin its countdown to expire as soon as the cache is loaded. A Sliding policy tells the cache to reset the expiration countdown each time the cached data is accessed. Caching techniques can help optimize your applications in situations where you are loading data that is not very volatile. For example, it would be advantageous to use caching in a data source control that loads a DropDownList of states, cities, or even product categories, since they do not change frequently.

The ObjectDataSource control wraps the code that hooks into business objects to invoke business methods. It also works in concert with data bound controls such as the GridView to perform paging, sorting, and data changes that had to be coded by hand in ASP.NET 1.x.

Wrapping Up

The improvements in ASP.NET 2.0, specifically in the areas of data-source and data-bound controls, significantly reduce the amount of code needed to produce a data-filled grid with full paging, sorting, and editing features. Even though the data source controls eliminate a ton of code that you used to have to write manually, you can still write code to interact with the data source controls. While you can just point and click to create a data-driven Web page, you can also write code to use the data source controls' events such as the Selected, Selecting, Updated, or Updating events.

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

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


Cutting Edge: Custom Script Callbacks in ASP.NET

Cutting Edge
Custom Script Callbacks in ASP.NET
Dino Esposito

Code download available at: CuttingEdge0501.exe (161KB)
Contents
Inspired by GridView Controls
The ABC of Controls Script Callbacks
The CallbackValidator Control
How the Control Works
Building the Control
Working Around State Issues
JavaScript Files as Embedded Resources
Conclusion

ASP.NET client callbacks represent a neat and elegant way to execute server-side code without posting and refreshing the current page. I discussed ASP.NET callbacks in the August and December 2004 installments of Cutting Edge, considering them from the perspective of rendered pages making background callbacks to the server, sending input data to the relevant page, and receiving a response. The response string can then be processed by the client however it sees fit, often manipulating the rendered page content through the Dynamic HTML (DHTML) object model and a callback JavaScript function embedded in the page.

While that use of callbacks is exciting, they can do more. A script callback mechanism can also add advanced functionalities to a server control. By implementing a couple of interfaces, any custom control can be endowed with script callback capabilities to use background round-trips to collect server data and update the user interface—the topic I'll cover this month.

Inspired by GridView Controls

If you've read my recent ASP.NET 2.0 GridView feature article, you know that the GridView control does not require you to refresh the whole page in order to display a new page of records. In fact, the GridView control provides an advanced engine for paging and sorting that is based on ASP.NET script callbacks. The data for the new page downloads behind the scenes, invisible to the user. Once it reaches the client, the data is collected by a JavaScript function and used to update the current view.

Paging and sorting callbacks are not 100 percent client-side solutions (if you need a purely client-side implementation, see the one Jeff Prosise built in the Wicked Code column in February 2004). The GridView's page and sort callbacks work on demand, downloading only the data needed; the whole data source is not downloaded onto the client. You still pay the price of a round-trip, but you're guaranteed the most current data even if that data has recently been updated on the server.

Having discovered that ASP.NET controls can support script callback capabilities had me all a-tingle for a while, and made me rush to figure out how to build my own.

Incidentally, the GridView is not the sole ASP.NET 2.0 control to sport a similar capability. Other view controls, such as TreeView, DetailsView, and FormView, provide the same feature out of the box. As a developer using a callback-enabled control, you don't need to cope with server-side code or worry about writing and embedding JavaScript code in the hosting page. The control takes care of everything and exposes an intuitive programming model through which you can control the script callback mechanism.

The ABC of Controls Script Callbacks

The ASP.NET script callback mechanism consists of two key elements: the server-side code that executes in response to a user action and the JavaScript callback code on the client that processes the results generated by the server-side event. In a scenario where a page calls back to itself, like the one I considered in the aforementioned articles, you can attach some ASP.NET-generated script code to a page button that performs a postback that's invisible to the user. Because the target of this request is the current page, the page posts to itself, similar to how it would in an ordinary postback event, though with an abbreviated page lifecycle. The page must implement the ICallbackEventHandler interface so that a method with a predefined signature can be called to generate results for the client.

So how does the scenario differ when a control triggers the out-of-band call? In this case, the target URL of the "invisible" postback is the URL of the page that hosts the caller control. The control must implement the ICallbackEventHandler in order to provide a method that generates some results for the client. Likewise, the control is responsible for injecting in the hosting page any JavaScript code that is needed to process results and refresh the page.

A callback-enabled control is simply a control that implements the ICallbackContainer and ICallbackEventHandler interfaces, both of which have one method. The ICallbackContainer interface has a method to return the script code that triggers the remote call; the ICallbackEventHandler interface provides the server-side code to execute during the call. ICallbackEventHandler is the same interface that a callback-enabled page must implement. The declaration for a sample custom control that implements callback interfaces is shown in the following code:

public class CallbackValidator : WebControl,
    INamingContainer, ICallbackContainer, ICallbackEventHandler

In the implementation of the ICallbackContainer interface you may need to place a call to the page's GetCallbackEventReference method to obtain a correct JavaScript call to start the server event. I'll return to this later.

The CallbackValidator Control

To understand callback-enabled server controls, let's take a look at an example of a custom validator control powered by ASP.NET script callbacks. In ASP.NET, validation controls are used to check and verify the input of the form fields defined within a Web page. The validator is a server control that inherits from the BaseValidator class which, in turn, inherits from Label.

Each validation control references an input control located elsewhere in the page. When the page is about to be submitted, the contents of any monitored server control is passed to the validator for further processing. Each validator performs a different type of verification. For example, the CompareValidator control compares the user's entry against a fixed value using a comparison operator such as less-than, equal-to, or greater-than. The RangeValidator ensures that the user's entry falls within a specified range whereas the RegularExpressionValidator validates the user's entry only if it matches a pattern defined by a regular expression.

Normally, validation takes place on the server. However, ASP.NET also provides a complete client-side implementation for most validation controls and allows the user to write custom client-side script for the rest. This allows DHTML-enabled browsers, such as Microsoft® Internet Explorer version 4.0 and later, to perform validation on the client as soon as the user tabs or clicks out of a monitored input field. In many cases, client-side validation is powerful enough to detect many significant errors and notify users. For example, a RequiredFieldValidator control verifies that the given field is not left empty. There is no need to post back to the server to verify the current value.

If client-side validation is turned on, the page won't post back until all the input fields contain valid data. In order to run secure code and to prevent malicious and underhanded attacks, you should still validate data on the server; server-side validation is always performed by the validator controls even if client-side validation is also performed. Besides, not all types of validation can be accomplished on the client. In fact, if you need to validate against a database, there's no choice but to post back to the server. And this is just where the rub lies.

A regular postback involves the page as a whole. The entire view state is uploaded, the entire page is processed, and the same large response is generated, downloaded, and rendered. Wouldn't it be nice if you could issue an out-of-band, optimized request to the server and check the state of only the controls under validation?

In ASP.NET there's no such a control. So let's write one that I'll name CallbackValidator. CallbackValidator is a custom ASP.NET 2.0 control I built to demo how a control can implement out-of-band calls to the host page and handle the event itself on the server.

When I embarked on this project, I actually had a not-so-ambitious objective: my goal was simply to modify the CustomValidator standard control. For the record, the CustomValidator control employs programmatically defined validation logic to check the validity of the user's entry. You use this approach when the values to check against are not known beforehand. The original intention of the CallbackValidator control was to offer a way to perform server-side validation without posting back the whole page. I was halfway finished with modifying my control when I realized that with no significant extra effort I could have had a custom button-like control capable of validating a number of input fields on the server without posting back the whole page. This behavior is what the CallbackValidator control is all about.

Before I delve into the control's nuts and bolts, take a look at Figure 1. The Submit button on this page just posts all the values to the server the usual way. In practice, values will be processed on the client and if all of them pass, the control passes to the server where all control input will be validated using the server-side validation code, if any. The Validate button triggers an out-of-band call to the Web server and validates only the specified input controls. When it returns, you know which values have passed validation by the server. In Figure 1, for example, you'll know whether the user ID is already taken before you try to submit the rest of the data.

Figure 1 Input Form with a Callback-Enabled Validation
Figure 1 Input Form with a Callback-Enabled Validation

Figure 2 shows the source code of this page. As you can see, it contains an HTML server form, a few textboxes (each bound to a standard validation control), and an instance of the custom CallbackValidator control. This control is actually responsible for creating and displaying the Validate button.

Figure 2 Validation Code and Forms (Close Figure 2)
<%@ Page Language="C#" MasterPageFile="~/MsdnMag.master"
    CompileWith="Default.aspx.cs" ClassName="Default_aspx"
    Title="Cutting Edge (Jan05)" %>
<%@ Register TagPrefix="cc1" Namespace="MsdnMag.Controls"
    Assembly="CallbackControls" %>

<asp:Content Runat="server" ContentPlaceHolderID="PageDescription">
    <asp:Label Runat="Server" ID="Desc">Demonstrates controls that
    implement client-side callback functionality</asp:Label>
</asp:Content>

<asp:Content Runat="server" ContentPlaceHolderID="PageBody">
    <h1>Register for a free account</h1>
    <table cellpadding="0"><tr>
            <td><b>User ID</b></td>
            <td><asp:TextBox ID="UserId" Runat="server" Text="dino" />
                <asp:CustomValidator ID="valUserId" Runat="Server"
                    ControlToValidate="UserId" ValidateEmptyText="true"
                    ErrorMessage="The user ID already exists or is empty"
                    OnServerValidate="EnsureUnique" Text="*" /></td>
        </tr>
        <tr>
            <td><b>Email address</b></td>
            <td><asp:TextBox ID="Email" Runat="server" />
                <asp:RegularExpressionValidator ID="valEmail"
                   Runat="Server" ControlToValidate="Email"
                   Text="*" ErrorMessage=
                   "The value does not appear to be a valid email address"
                   ValidationExpression=
                   "[a-zA-Z_0-9.-]+\@[a-zA-Z_0-9.-]+\.\w+" /></td>
        </tr>
        <tr>
            <td><b>Password</b></td>
            <td><asp:TextBox ID="Pswd" Runat="server"
                   TextMode="Password" />
                <asp:CustomValidator ID="valPswd" Runat="Server"
                    ControlToValidate="Pswd"
                    Text="*" ErrorMessage="Enter a strong
                        password (8+ chars including lower and
                        upper case, digits and symbols"
                    OnServerValidate="EnsureStrong" /></td>
        </tr>
        <tr>
            <td bgcolor="lightyellow">
                <cc1:CallbackValidator
                    ID="CallbackValidator1" Runat="server"
                    ShowDetailedInfo="true" ButtonText="Validate" /></td>
            <td bgcolor="lightyellow" align="right">
                <asp:Button ID="Button1" Runat="server"
                    Text="Submit" Font-Bold="True" /></td>
        </tr></table>
</asp:Content>

How the Control Works

The CallbackValidator control inherits from WebControl and implements the INamingContainer interface. In addition, it implements the ICallbackContainer and ICallbackEventHandler interfaces for callback support.

The ICallbackContainer interface requires a method, GetCallbackScript, declared like so:

string GetCallbackScript(IButtonControl buttonControl, string argument)
GetCallbackScript takes two arguments. The first is a reference to the page's control that is expected to trigger the callback. The second argument (a string) represents any context the caller wants to pass to the method to help with the construction of the output. As the name suggests, the GetCallbackScript method prepares and returns a string with the JavaScript function call to attach to the specified button control to trigger the remote call.

The button control argument allows you to specify exactly which button in the control's UI you're making the JavaScript call for. The sample CallbackValidator control has just one clickable button; the GridView control has many, one for each link button in the pager or in the header. In ASP.NET 2.0, all controls that act like a button on a form are required to implement a new interface—IButtonControl. The interface is detailed in Figure 3 and is implemented by the following Web controls: Button, LinkButton, and ImageButton. By design, HTML button controls do not implement the interface. Note that in the Microsoft .NET Framework 1.x, the IButtonControl interface exists (albeit with a radically different set of members) only for Windows® Forms button controls.

Figure 3 IButtonControl Interface in ASP.NET 2.0 (Close Figure 3)

Properties
Name | Description
CausesValidation | Indicates whether the content of the input fields should be validated when the button is clicked.
CommandArgument | Gets or sets an optional parameter passed to the Command event along with the associated CommandName.
CommandName | Gets or sets the command name associated with the button that is passed to the Command event.
PostBackUrl | Gets or sets the URL of the page to post to from the current page when the button is clicked.
SoftkeyLabel | Gets or sets the text to display for a soft key label. Ignored when the button renders to a device that does not support soft keys.
Text | Gets or sets the caption of the button.
ValidationGroup | Gets or sets the group of controls for which the button causes validation when it posts back to the server.
Visible | Indicates whether the button is rendered on the page.
Events
Name | Description
Click | Occurs when the button is clicked. This event is commonly used when no command name is associated with the button.
Command | Occurs when the button is clicked. The event handler receives an argument of type CommandEventArgs containing data related to this event.

The second interface required by a callback-enabled control is ICallbackEventHandler—the same interface required on pages that support script callbacks. The interface consists of one method:

string RaiseCallbackEvent(string eventArgument)

The method receives input values in the form of a string, does some server-side work, and returns its response again in the form of a string. What matters here is that both input and output data can travel packed as strings; the real content and format of the strings is up to the coder.

Before I discuss the implementation of the CallbackValidator control, take a look at Figure 4, which illustrates how the control fits in the page's HTTP handler that processes requests for ASPX resources. The CallbackValidator control looks like a button with some script code attached. The script code is just what its GetCallbackScript method returns. When clicked, the button (Validate) fires the background postback sending view state, current input values, plus a couple of custom strings named CALLBACKPARAM and CALLBACKID. The former contains the input value for RaiseCallbackEvent as created in the body of the GetCallbackScript method, and CALLBACKID serves to identify the server-side object to handle the server event. Once on the server, the page's HTTP handler that picks up the request from the ASP.NET runtime tries to locate a control with that ID that implements ICallbackEventHandler. If successful, the control's RaiseCallbackEvent method is invoked and its output returned to the client. If the CALLBACKID targets the page, the HTTP handler sees if the page implements the interface and then proceeds as usual.

Figure 4 Callback Validator Control (Close Figure 4)

Building the Control

CallbackValidator is a composite control whose user interface consists of a simple pushbutton. You can easily extend this aspect of the control by adding a few properties to set the style of the button—link, push, image, or whatever. The text to be shown on the button is set by the ButtonText property. A collection property named ControlsToValidate gathers the IDs of all page validators to be tested on the server during the callback. The property is implemented as a StringCollection type and is empty at the beginning. The code in Figure 5 only lets you add control IDs at run time, which you would typically do in the Page_Load event:

void Page_Load(object sender, EventArgs e) {
   CallbackValidator1.ControlsToValidate.Add("valUserId");
   CallbackValidator1.ControlsToValidate.Add("valEmail");
}

Figure 5 RaiseCallbackEvent (Close Figure 5)
public class CallbackValidator : WebControl,
    INamingContainer, ICallbackContainer, ICallbackEventHandler
{
    private StringCollection _controlsToValidate;

    [Description("The text to be shown on the button")]
    public string ButtonText
    {
        get { return Convert.ToString(ViewState["ButtonText"]); }
        set { ViewState["ButtonText"] = value; }
    }

    [Description("Whether to show the full validation error message")]
    public bool ShowDetailedInfo
    {
        get { return Convert.ToBoolean(ViewState["ShowDetailedInfo"]); }
        set { ViewState["ShowDetailedInfo"] = value; }
    }

    [Browsable(false)]
    public StringCollection ControlsToValidate
    {
        get {
            if (_controlsToValidate == null)
                _controlsToValidate = new StringCollection();
            return _controlsToValidate;
        }
    }

    protected override void CreateChildControls()
    {
        Controls.Clear();

        // Create the button to start the callback operation
        Button b = new Button();
        b.Text = ButtonText;
        b.CopyBaseAttributes(this);
        if (ControlStyleCreated)
            b.ApplyStyle(ControlStyle);

        // Attach some script code to trigger the remote call
        ICallbackContainer cont = this as ICallbackContainer;
        b.OnClientClick = cont.GetCallbackScript((IButtonControl) b, "");

        // Add the button to the control's hierarchy for display
        Controls.Add(b);

        // Inject any needed script code into the page
        EmbedScriptCode();
    }

    protected override void Render(HtmlTextWriter writer)
    {
        EnsureChildControls();
        base.Render(writer);
    }

    ...
}

Note that the collection class doesn't persist its contents to the view state. For this reason, you must always reinitialize it when a request comes in. Note also that you should add validation controls, not input controls, to the collection. During the remote call, the CallbackValidator control will invoke the Validate method on associated controls and store the responses for the client callback. The CallbackValidator control works with existing validators making an out-of-band call to test them all; it's not actually a new type of validator itself.

As you can see in Figure 5, the CallbackValidator control creates a Button control and attaches some code to its OnClientClick property. OnClientClick is a new property introduced in ASP.NET 2.0 to add a JavaScript call to the HTML onclick event. In ASP.NET 2.0, the following two lines of code are completely equivalent:

button.Attributes["onclick"] = js; // ASP.NET 1.x; still works in 2.0

button.OnClientClick = js; // ASP.NET 2.0
The code to associate with the validate button is obtained through a specific method wrapped in the ICallbackContainer interface. Note that as of Beta 1, the use of the ICallbackContainer interface is not mandatory, but using it does help to keep code neat and clean. I didn't use it in last month's example and it is not even mentioned in the MSDN documentation for Beta 1 that discusses script callbacks. Nonetheless, ASP.NET controls that benefit from script callbacks (mostly the GridView) implement it. The only component that uses the ICallbackContainer interface is the control itself, meaning that you can easily write a callback-enabled control that doesn't use that interface.

The JavaScript function call that GetCallbackScript returns comes from the following statement:

Page.GetCallbackEventReference(
    this, args, "CallbackValidator_UpdateUI", "null"));
The first parameter (this) indicates the current control and sets the CALLBACKID field in the request being issued. The second parameter (args) is the input string for the server-side RaiseCallbackEvent method. The third parameter is the name of the JavaScript callback function which is used to process the output of the RaiseCallbackEvent method. Finally, the fourth parameter is null in this case, but represents a JavaScript object to be passed as the context of the callback function.

The CallbackValidator control must ensure that the JavaScript callback is defined in the hosting page and must make the decision about the format and contents of the args parameter. There is just one type of information the RaiseCallbackEvent implementation needs from its client caller: the list of validators to test. Here's the code that concatenates all validators' IDs in a pipe-separated string:

int i = 0;
StringBuilder sb = new StringBuilder("");
foreach (string s in ControlsToValidate)
{
   if (i>0) sb.Append("|");
   sb.Append(s);
   i++;
}
string args = String.Format("'{0}'", sb.ToString());

An example of JavaScript code bound to the Validate button in Figure 2 might look like the following:

WebForm_DoCallback(
    'ctl00$PageBody$CallbackValidator1',
    'valUserId|valEmail',
    CallbackValidator_UpdateUI,
    null,
    null);
Note that the ID of the CallbackValidator control is mangled by the server so that it can uniquely identify every control on the page.

Working Around State Issues

As in Figure 4, the request posted back contains a few input fields. Aside from the aforementioned CALLBACKID and CALLBACKPARAM fields, the request includes a few more input fields. More precisely, it includes all the input fields in the form, plus the two specific to the callback operation. In other words, the view state is posted back along with the current values of the input fields (textboxes, dropdown lists, and so forth).

The page HTTP handler restores the view state and posted values before it checks the callback status of the request and figures out which object (Page or control) will have RaiseCallbackEvent called. In light of this, you may assume that the code defined in the RaiseCallbackEvent method will execute in a consistent and updated state. In particular, you may assume that all validators invoked by the RaiseCallbackEvent method of the CallbackValidator control will test the current values of their bound input controls. As the section title may suggest, this is not the case. Take a look at the code of RaiseCallbackEvent in Figure 6.

Figure 6 The CallbackValidator Control—Base Code (Close Figure 6)
public string GetCallbackScript(
    IButtonControl buttonControl, string argument)
{
    // Prepare the input for the server code
    int i = 0;
    StringBuilder sb = new StringBuilder("");
    foreach (string s in ControlsToValidate)
    {
        if (i>0) sb.Append("|");
        sb.Append(s);
        i++;
    }
    string args = String.Format("'{0}'", sb.ToString());
    string js = String.Format("javascript:{0};{1};{2}; return false;",
        "__theFormPostData = ''",
        "WebForm_InitCallback()",
        Page.GetCallbackEventReference(
            this, args, "CallbackValidator_UpdateUI", "null"));
    return js;
}

public string RaiseCallbackEvent(string eventArgument)
{
    // Execute the server-side code
    // Receives a string like this:
    //     ctl|ctl|...ctl
    // Sends back a string like this:
    //     ctl:valid:msg:tip|...ctl:valid:msg:tip
    StringBuilder sb = new StringBuilder("");
    string[] controlsToValidate = eventArgument.Split('|');
    int i = 0;
    foreach (string s in controlsToValidate)
    {
        BaseValidator val = NamingContainer.FindControl(s)
            as BaseValidator;
        if (val != null)
        {
            val.Validate();

            if (i > 0) sb.AppendFormat("|");
            sb.AppendFormat("{0}:{1}:{2}:{3}",
                val.ClientID,
                (val.IsValid ? "1" : "0"),
                (val.IsValid ? "" : GetErrorText(val)),
                (val.IsValid ? "" : GetTooltip(val)));
        }
        i++;
    }

    return sb.ToString();
}

private void EmbedScriptCode()
{
    Assembly dll = Assembly.GetExecutingAssembly();
    StreamReader reader;
    reader = new StreamReader(dll.GetManifestResourceStream(
        "MsdnMag.CallbackValidator.js"));
    string js = reader.ReadToEnd();
    reader.Close();

    if (!Page.ClientScript.IsClientScriptBlockRegistered(
            "CallbackValidator"))
        Page.ClientScript.RegisterClientScriptBlock(
            typeof(CallbackValidator), "CallbackValidator", js, true);
}

The method retrieves the validator control and invokes its Validate method. The method does its job (what exactly depends on the type of the validator) and sets the IsValid property to either True (valid) or False (invalid). Next, the RaiseCallbackEvent method builds its response to the client page. The return string is a pipe-separated collection of strings, each with the following form:

controlID:valid (0/1):message:tooltip
The first token is the client ID of the control that is the fully qualified ID that takes into account any mangling due to Master Pages and naming containers. The second token is 0 or 1 depending on the value of IsValid. The third token is the message the validator would display after a full postback. This corresponds to the validator's Text (default) or ErrorMessage property. I also force a * string if both are empty. By design, Text is expected to contain some text to simply mark the field as invalid. ErrorMessage, instead, provides a more detailed explanation of the error. If the CallbackValidator's ShowDetailedInfo property is true, I use the ErrorMessage string as a tooltip, as shown in Figure 7.

Figure 7 Validation Error Message
Figure 7 Validation Error Message

So where's the state-related hassle? This machinery works great on paper, but not with real values. While debugging, I realized that the results of the validation tests were completely unreliable. For example, the User ID textbox is designed to accept anything but "Dino" and empty strings. Well, it works great with regular postbacks, but not if callback validation is used. Some well-placed breakpoints showed that all textboxes retain their original values and ignore what you may have typed before attempting to validate. The problem is not with the view state, but with posted values. The page machinery works great as usual; it just doesn't receive what I perceived to be the correct values from the client.

If you scroll the HTML source code of a page that uses ASP.NET callbacks, you see that upon page loading a call is made to a JavaScript function named WebForm_InitCallback. This function is part of the ASP.NET 2.0 infrastructure and is injected into the page through the WebResource.axd system handler. A look at the source code of this function is in order. (See last month's column for details on how to get it.) Basically, WebForm_InitCallback builds the body of the POST request when the page loads. The body of the page is a string (its name is __theFormPostData) filled with the contents of the view state and all of the input fields in the form. The code is correct; however it executes at a time I wasn't expecting! The content of the input fields is collected at load time and is not updated with user-supplied values when the post back takes place. That's why the server state appeared to be incorrect. To work around this issue, I simply repeated the call to WebForm_InitCallback before starting the out-of-band call (see Figure 6). Note that this is in fact the expected behavior rather than any sort of bug. The argument against the system calling WebForm_InitCallback before the out-of-band call is for scenarios where the user wants to perform the callback in the context of the data that was originally sent down from the server.

JavaScript Files as Embedded Resources

To top off the column, let me discuss a nifty technique that greatly simplifies JavaScript code injection in ASP.NET 2.0 custom controls. The technique also works in ASP.NET 1.x. The idea is simple: write your JavaScript code in regular JS files and add them to the project as embedded resources. (Set the Build Action property in the Visual Studio® .NET Properties window). Also, prefix the resource name with the component's namespace. Next, when you need to use the script in the code, do what the EmbedScriptCode method does in Figure 6. You use a bit of reflection, but you gain a lot in terms of code maintenance and readability.

Conclusion

ASP.NET script callbacks are an extremely powerful feature that can save time on each page update. Although a server request is issued, the whole page remains intact in the browser's window. The advantage is two-fold. First, the user has the illusion that no postback is necessary and continues reading or working with the page. Second, only a few page fragments are refreshed (using the HTML object model). In the August 2004 and December 2004 columns, I provided in-depth coverage of the basic feature. This month I demonstrated how to implement callbacks in custom controls to provide more flexibility in the controls you build.

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

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


Test Run: Lightweight UI Test Automation with .NET

Test Run
Lightweight UI Test Automation with .NET
James McCaffrey

Code download available at: TestRun0501.exe (131KB)
Contents
The Application Under Test
The Test Automation Script
Manipulating the Application Under Test
Checking the Application State
Discussion

Manual user interface testing is one of the most fundamental types of software testing and it's the kind of testing that most software engineers first experience. Paradoxically, automated user interface tests are probably the most technically challenging kind of test to write. The Microsoft® .NET environment provides you with many ways to write automated user interface test automation. One common and useful approach is recording keystrokes, mouse movements, and clicks and then playing them back to the application to ensure that it performs as expected. (For more information on this approach, see John Robbins's Bugslayer column in the March 2002 issue of MSDN®Magazine. Paul DiLascia's column in this issue of MSDN Magazine also demonstrates how to use .NET to send this sort of input to another application.) In this month's column, I'll explore another approach to writing lightweight UI test automation for .NET applications.

The best place to start the discussion is with a screen shot. Figure 1 shows that I have a dummy application to test. It is a color mixer app which allows a user to type a color into a textbox control, then type or select a color in a combobox, click the button, and have the listbox display a message that represents the result of "mixing" the two colors. In Figure 1, red and blue produces purple according to the application. The UI test automation is a console application that launches the form under test, simulates a user moving the application form, sizes and resizes the application form, sets values for the textbox and combobox controls, and clicks the button control. The test automation checks the resulting state of the application under test, verifies that the listbox control contains the correct message, and logs a "pass" result. I captured the screen shot in Figure 1 just before the test automation simulates a user clicking on File | Exit, which closes the application under test.

Figure 1 Form UI Test Automation
Figure 1 Form UI Test Automation

In the sections that follow I will briefly describe the dummy application I'm testing, explain how to launch the application's form from the test automation program using reflection and the System.Windows.Forms.Application class, show you how to simulate user actions and check application state using methods in the System.Reflection namespace, and describe how you can extend and modify the test system to meet your own needs. I think you'll find the ability to write lightweight UI test automation quickly to be a useful addition to your skill set no matter what role you play in the software production environment. Additionally, these same techniques can be incorporated into your own unit testing harness and are relevant even if you're using an existing framework like NUnit.

The Application Under Test

Let's look at the application under test so you'll understand the goal of the test automation. The color mixer application under test is a simple Windows® form. I used C# to code the application, but the UI automation techniques I'll show you apply to applications written using any .NET-targeted language. I accepted the Visual Studio® .NET default control names of Form1, textBox1, comboBox1, button1, and listBox1. Of course, in a real application you'd change the names of the controls to reflect their functionality. I added three dummy menu items: File, Edit, and View. The heart of the app under test is the code listed in Figure 2.

Figure 2 Color Mixer Application Code (Close Figure 2)
private void button1_Click(object sender, System.EventArgs e)
{
  string tb = textBox1.Text;
  string cb = comboBox1.SelectedItem.ToString();

  if (tb == cb )
    listBox1.Items.Add("Result is " + textBox1.Text);
  else if (tb == "red" && cb == "blue" || tb == "blue" && cb == "red")
    listBox1.Items.Add("Result is " + "purple");
  else
    listBox1.Items.Add("Result is " + "black");
}

When a user clicks on the button1 control, the application grabs the values in the textBox1 and comboBox1 controls. If the two color strings match, a message with that color is displayed. If the textbox and combobox controls contain "red" and "blue", a result message with "purple" is displayed. If any other color combinations are in the textbox and combobox controls, a result message with "black" is displayed. Because this is just a dummy app for demonstration purposes and I wanted to keep the code as short as possible, I didn't check my input arguments as I would have for a realistic app. Although this app is extremely simplistic, it has most of the fundamental characteristics of Windows-based apps needed to demonstrate automated UI testing.

Manually testing even this minimal application through its user interface would be tedious, error prone, time consuming, and inefficient. You'd have to enter some inputs, click the button control, visually verify the result message, and record the result into an Excel spreadsheet or other data store. Because the app accepts free-form user input into the textbox control, you'd essentially have an infinite volume of possible test input, so you'd have to test hundreds or even thousands of inputs to be satisfied that you understood the behavior of the application. On top of all this, every time there was a change in the application code, you'd have to perform the same manual tests all over again. Writing unit tests is a much better approach as these tests allow you to simulate a user exercising the app and then determine if the app has responded correctly.

The Test Automation Script

The overall structure of the test automation harness is shown in Figure 3 and an outline of the code is shown in Figure 4. I used C# here, but you could easily modify the code to be any .NET-based language.

Figure 4 UI Test Automation Harness Code (Close Figure 4)
using System;
using System.Windows.Forms;
using System.Reflection;
using System.Threading;

namespace RunScenario
{
  class Program
  {
    static Form testForm = null;
    static BindingFlags flags = BindingFlags.Public |
                                BindingFlags.NonPublic |
                                BindingFlags.Static |
                                BindingFlags.Instance;
    static int delay = 1500;

    static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("\nStarting UI test scenario");
        // launch the application
        // move and resize the form
        // set textBox1 and comboBox1
        // click button1
        // check message in listBox1, display pass or fail result
        // click File->Exit
      }
      catch(Exception ex)
      {
        Console.WriteLine("Error: " + ex.Message);
      }
    }

    static void SetControlPropertyValue(string controlName,
        string propertyName, object newValue)
    {
      // code here to set a control property
    }

    static void SetFormPropertyValue(string propertyName, object newValue)
    {
      // code here to set a form property
    }

    static object GetControlPropertyValue(string controlName,
        string propertyName)
    {
      // code here to access a control property
    }

    static void InvokeMethod(string methodName, params object[] parms)
    {
      // code here to click a button
    }

    static Form LaunchApp(string exePath, string formName)
    {
      // code here to launch the application
    }
  }
}
Figure 3 UI Test Automation Structure
Figure 3 UI Test Automation Structure

I begin by adding and declaring references to the System.Windows.Forms, System.Reflection, and System.Threading namespaces. Because the System.Windows.Forms.dll is not referenced in a console application by default, to use the classes in this namespace you need to add a project reference to the System.Windows.Forms.dll file. The System.Windows.Forms namespace contains the Forms class as well as the Application class, both of which I use in this solution. I use classes in System.Reflection to get and set the values of the form's controls and to invoke methods associated with the form. I use methods in System.Threading to launch the form from the console application test harness.

I declare three class-scope objects because they're used by several methods in the test harness:

static Form testForm = null;
static BindingFlags flags = BindingFlags.Public |
                            BindingFlags.NonPublic |
                            BindingFlags.Static | BindingFlags.Instance;
static int delay = 1500;

Because the color mixer application is just one Windows Form, I declare a Form object to represent it. The BindingFlags object is used by many methods in the System.Reflection namespace as a filter. I set an int delay variable to 1500 (milliseconds) to feed to the Thread.Sleep method so I can pause 1.5 seconds at various points in the test automation. To launch the app under test I use this code:

Console.WriteLine("\nLaunching WinApp under test");
string exePath = "C:\\FormUIAutomation\\WinApp\\bin\\Debug\\WinApp.exe";
testForm = LaunchApp(exePath, "WinApp.Form1");
I've defined a LaunchApp method and its helper method RunApp in Figure 5. There aren't very many lines of code but they are very powerful. Notice that for simplicity I hardcoded the path to the executable of the application under test (in your own tests, you'd want to parameterize this information to make the test automation more flexible). The LaunchApp method accepts the path to the application executable and the name of the application form, and returns an object representing the form. LaunchApp creates an instance of an Assembly object using the Assembly.LoadFrom static method rather than by using an explicit constructor call.

Figure 5 Launching the App (Close Figure 5)
static Form LaunchApp(string exePath, string formName)
{
  Thread.Sleep(delay);
  Assembly a = Assembly.LoadFrom(exePath);
  Type formType = a.GetType(formName);
  Form resultForm = (Form)a.CreateInstance(formType.FullName);
  Thread t = new Thread(new ThreadStart(new AppState(resultForm).RunApp));
  t.ApartmentState = ApartmentState.STA;
  t.IsBackground = true;
  t.Start();
  return resultForm;
}

private class AppState
{
  public AppState(Form f) { FormToRun = f; }
  public readonly Form FormToRun;
  public void RunApp()
  {
    Application.Run(FormToRun);
  }
}

Next, the Assembly.GetType method returns a type that represents the application form, and then I use the Assembly.CreateInstance method to create a reference to the form under test. Then I spin up a new Thread to actually launch the application form. The Application.Run method starts a message loop on the current thread; since I want to be able to perform work while the Form is visible, I need to run Application.Run on its own thread so that its loop doesn't block my progress. By using this technique, the test automation console application harness and the form are running on different threads but in the same process. This way they can communicate with each other—in other words, the test harness can send instructions to the Windows Form.

Manipulating the Application Under Test

After I've launched the application under test, I simulate a user manipulating the application form. The example test scenario begins by moving and resizing the form like so:

Console.WriteLine("Moving form");
SetFormPropertyValue("Location", new System.Drawing.Point(200,200));
SetFormPropertyValue("Location", new System.Drawing.Point(500,500));

Console.WriteLine("Resizing form");
SetFormPropertyValue("Size", new System.Drawing.Size(600,600));
SetFormPropertyValue("Size", new System.Drawing.Size(300,320));
All the work is done by method SetFormPropertyValue (see Figure 6). I use the Object.GetType method to create a Type object that represents the application form, and then use that object to get a PropertyInfo object that references a property on the form such as the Location property or the Size property. Once I have the property information object, I can manipulate it using the PropertyInfo.SetProperty method. SetProperty accepts three parameters. The first two are what you might expect—a reference to the object containing the property that will be changed and a reference to the new value of the property.

Figure 6 SetFormPropertyValue (Close Figure 6)
static void SetFormPropertyValue(string propertyName, object newValue)
{
  if (testForm.InvokeRequired)
  {
    Thread.Sleep(delay);
    testForm.Invoke(
      new SetFormPropertyValueHandler(SetFormPropertyValue),
      new object[]{propertyName, newValue});
    return;
  }
  Type t = testForm.GetType();
  PropertyInfo pi = t.GetProperty(propertyName);
  pi.SetValue(testForm, newValue, new object[0]);
}

delegate void SetFormPropertyValueHandler(
    string propertyName, object newValue);

The third parameter is necessary because some properties, such as the Items property of the listbox control, are indexed. Moving and resizing the form as I've done here isn't really relevant for testing the application's functionality but I do it to show you how it's done in case your test scenario requires it. Note, too, that I'm taking advantage of the ISynchronizeInvoke interface exposed by the Form class (actually, by its base Control class). You should only access properties of a control (that includes a Form) from the thread that owns the control's underlying window handle. For the form under test, that thread is the one I spun up on which to run Application.Run. Since my test harness is running on a separate thread, I need to marshal my access to properties and methods on the controls to that thread, and the control's Invoke method and InvokeRequired property make that a piece of cake (both this method and property are part of the ISynchronizeInvoke interface). For more information on ISynchronizeInvoke, see Ian Griffith's article in the February 2003 issue of MSDN Magazine, at Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads.

Now I'm ready to simulate a user typing a color into the textbox control and selecting a color from the combobox control:

Console.WriteLine("\nSetting textBox1 to 'yellow'");
SetControlPropertyValue("textBox1", "Text", "yellow");
Console.WriteLine("Setting textBox1 to 'red'");
SetControlPropertyValue("textBox1", "Text", "red");

Console.WriteLine("Selecting comboBox1 to 'green'");
SetControlPropertyValue("comboBox1", "SelectedItem", "green");
Console.WriteLine("Selecting comboBox1 to 'blue'");
SetControlPropertyValue("comboBox1", "SelectedItem", "blue");

I set textBox1 to "yellow" and then to "red", and then set comboBox1 to "green" and then "blue". All the real work is done by a SetControlPropertyValue method shown in Figure 7.

Figure 7 SetControlPropertyValue (Close Figure 7)
static void SetControlPropertyValue(
  string controlName, string propertyName, object newValue)
{
  if (testForm.InvokeRequired)
  {
    Thread.Sleep(delay);
    testForm.Invoke(
      new SetControlPropertyValueHandler(SetControlPropertyValue),
      new object[]{controlName, propertyName, newValue});
    return;
  }
  Type t1 = testForm.GetType();
  FieldInfo fi = t1.GetField(controlName, flags);
  object ctrl = fi.GetValue(testForm);
  Type t2 = ctrl.GetType();
  PropertyInfo pi = t2.GetProperty(propertyName, flags);
  pi.SetValue(ctrl, newValue, new object[0]);
}

delegate void SetControlPropertyValueHandler(
    string controlName, string propertyName, object newValue);

I use the Thread.Sleep method to pause the test automation to make sure that the application under test is up and running. After creating a Type object that represents the application form's type, I retrieve information about a specified field (a control) on the Form object by using the Type.GetField method. Next I call the FieldInfo.GetType method to get a Type object that represents the control I want to manipulate. Once I have the control object I can manipulate it exactly like I manipulated the Form object by getting the control's PropertyInfo and then calling the SetValue method. As with SetFormPropertyValue, I need to ensure that all of the property changes I'm making are taking place on the correct thread.

Notice that the test automation does not directly simulate user actions at a very low level. For example, instead of simulating individual keystrokes into the textBox1 control the automation sets the Text property directly. And instead of simulating clicks on the comboBox1 control, the automation sets the SelectedItem property. This is a design limitation of my test automation system. To test in that way, you can follow the advice of John Robbins in the article mentioned earlier.

After simulating the actions of a user entering colors for the textbox and combobox controls, the automation simulates clicking of the button control:

Console.WriteLine("Clicking button1");
InvokeMethod("button1_Click", new object[]{null, EventArgs.Empty} );
I've defined the InvokeMethod method as shown in Figure 8.

Figure 8 Invoke Method (Close Figure 8)
static void InvokeMethod(string methodName, params object[] parms)
{
  if (testForm.InvokeRequired)
  {
    Thread.Sleep(delay);
    testForm.Invoke(new InvokeMethodHandler(InvokeMethod),
      new object[]{methodName, parms});
    return;
  }
  Type t = testForm.GetType();
  MethodInfo mi = t.GetMethod(methodName, flags);
  mi.Invoke(testForm, parms);
}
delegate void InvokeMethodHandler(
  string methodName, params object [] parms);

InvokeMethod gets a Type object representing the application form under test by calling the Object.GetType method. Then I get information about a specified method using Type.GetMethod and execute the specified method by calling MethodBase.Invoke . Invoke accepts two arguments. The first is the instance of the form on which to invoke the method, and the second is an array of parameters to the method. Now, in the case of a button control click method, the signature looks like this:

private void button1_Click(object sender, System.EventArgs e)
To satisfy the parameter requirements of the button1_Click method I need to pass an object that represents the sender and an EventArgs object that represents optional event data. For a button click I ignore the value of the first parameter, although for a real test system I should pass the control as the sender that was the cause of this method being invoked (the implementation of the method might depend on accessing that control, and this information is especially useful if this event handler method is used as the handler for multiple controls). For the second argument, I pass an empty EventArgs object.

Notice that the test automation simulates a button click by directly invoking the button control's associated method rather than by firing an event. When a real user clicks on a button it generates a Windows message which is processed by the control and turned into a managed event. This event causes a particular method (or set of methods) to be invoked. So the UI test automation will not catch the logic error if the application has the wrong method wired to a button click event (although every test would fail and you'd find the problem quickly). This can be rectified by getting the underlying multicast delegate for the event using reflection, and then using that delegate's GetInvocationList method to get a list of all of the delegates that will be invoked when the event is raised. Each delegate could then be invoked separately. Alternatively, you could use the event's EventInfo and its GetRaiseMethod method to get the MethodInfo for the method that raises the event, but that only returns a custom raise method, and the only Microsoft languages that support custom raise methods are C++ and Microsoft intermediate language (MSIL). Again, all of this can be avoided by using the send keys method discussed earlier.

Checking the Application State

After the automation sets the state of the application form by simulating user typing and clicking, it's time to check the system state to see if the application has responded correctly (see Figure 9).

Figure 9 Checking the State of the App Under Test (Close Figure 9)
Console.WriteLine(
  "\nChecking for state:\n 'red', 'blue', 'Result is purple'");
bool pass = true;
if ((string)GetControlPropertyValue("textBox1", "Text") != "red")
  pass = false;
if ((string)GetControlPropertyValue("comboBox1", "SelectedItem") !=
  "blue")
  pass = false;
if ( !((ListBox.ObjectCollection)GetControlPropertyValue("listBox1",
  "Items")).Contains("Result is purple") )
  pass = false;

if (pass) Console.WriteLine("\nUI test scenario result: PASS");
else Console.WriteLine("\nUI test scenario result: *FAIL*");

I set a Boolean variable, called "pass", to true—I will assume the application state is correct and examine the state, setting pass to false if anything is wrong. I check to make sure that the textBox1 control's Text property is correctly set to "red". Then I check to make sure comboBox1 has "blue" and listBox1 has the correct message, "Result is purple". If everything checks out, I print a pass message; otherwise I print a fail message.

The key to checking the state of the application system is a GetControlPropertyValue method I've coded as shown in Figure 10. I start by using Object.GetType to create a Type object that represents the application form. Then I use Type.GetField to grab information about a specified control. Next I use GetType again to get a Type object representing the control. Finally, I use GetProperty to get information about a specified property of the control and then get the value of the control property using the GetValue method. GetValue requires an indexed object argument because properties can be indexed (for example if I was trying to get an Items property of a listbox control).

Figure 10 GetControlPropertyValue (Close Figure 10)
static object GetControlPropertyValue(
  string controlName, string propertyName)
{
  if (testForm.InvokeRequired)
  {
    Thread.Sleep(delay);
    return testForm.Invoke(new GetControlPropertyValueHandler(
      GetControlPropertyValue),new object[]{controlName, propertyName});
  }
  Type t1 = testForm.GetType();
  FieldInfo fi = t1.GetField(controlName, flags);
  object ctrl = fi.GetValue(testForm);
  Type t2 = ctrl.GetType();
  PropertyInfo pi = t2.GetProperty(propertyName, flags);
  return pi.GetValue(ctrl, new object[0]);
}
delegate object GetControlPropertyValueHandler(
  string controlName, string propertyName);

Notice that checking the text in the listBox1 control is a little trickier than checking the text in the textBox1 control. I use my GetControlPropertyValue method to access the Items property and then check using the Contains method.

After I've examined the application state and logged a pass or fail result, I can easily exit the application under test:

Console.WriteLine("\nClicking menu File->Exit in 5 seconds . . . ");
Thread.Sleep(3500);
InvokeMethod("menuItem4_Click", new object[] {null, new EventArgs()} );
Even though the app under test will terminate when the test harness terminates because they're both running in the same process and because the application under test is running on a background thread, it's much better to explicitly exit the app from the test harness in order to explicitly clean up any system resources allocated.

Discussion

If you wanted UI test automation in the days before .NET, you really had only two choices. First, you could purchase a commercial UI automation tool. Second, you could create your own UI automation tools using Microsoft Active Accessibility (MSAA) APIs. The system I've presented complements these two other strategies quite nicely. There are several excellent commercial UI automation tools available to you. The advantage of these tools is their comprehensive list of features. The disadvantages are that you have to pay for them, they have a steep learning curve, and they don't allow you access to their source code in case you need to modify a feature. Using MSAA gives you full control over your automation tools but it can take a long time to learn. In fact, on several projects I worked on, the MSAA-based UI test automation was easily as complex as the application under test!

The automated UI test approach I've presented here has been used successfully on several medium- to large-scale products. It is very quick and easy to implement so it can be used early in the product cycle when the system under test is highly unstable. However, because this UI test system is relatively lightweight, it can't handle all possible UI testing situations. You can modify and extend this design in many ways. Because this presentation was intended to be primarily instructional, I've removed most error checking and I've hardcoded most information for clarity and simplicity. It's always important to add lots of error-checking code to test automation—after all, you're expecting to find errors.

Depending on your production environment, you may want to parameterize some parts of the test system. In testing terminology, the system I've presented is called a test scenario—a sequence of actions that manipulate the state of the application under test, (as opposed to a test case, which typically refers to a much smaller action like feeding an argument to a method and checking the return value.) To parameterize the scenario you could, for example, create an input file like this:

[set state]
textBox1:Text:yellow
textBox1:Text:red
comboBox1:SelectedItem:green
comboBox1:SelectedItem:blue
button1:button1_Click
[check state]
textBox1:Text:red
comboBox1:SelectedItem:blue
listBox1:Items:Result is purple
Then you could have your test automation read, parse, and use the data in this file. You could use XML or SQL for test scenario input data, too. The test system I've presented logs its results to a command shell. You can easily redirect results to a text file from a command line or recast the automation to log results directly.

The next generation of Windows, code-named "Longhorn," will have a new presentation subsystem code-named "Avalon." Avalon promises to take the UI test automation concepts I've presented to the next level. It is slated to provide platform-level support for automation of all UI elements and expose a consistent object model for all user controls. This will allow developers and testers to create extraordinarily powerful UI test automation quickly and easily. The techniques in this column give you a hint of how revolutionary Avalon will be.

Before .NET, writing automation was often a very resource-intensive task and test automation, especially UI test automation, was often relegated to the bottom of the product task priority list. But with .NET, it's now possible to write very powerful test automation in a fraction of the time it used to take.

Send your questions and comments for James to testrun@microsoft.com.

James McCaffrey works for Volt Information Sciences, Inc., where he manages technical training for software engineers working at Microsoft. He has worked on several Microsoft products including Internet Explorer and MSN Search. James can be reached at jmccaffrey@volt.com or v-jammc@microsoft.com.


The ASP Column: Determining Browser Capabilities in ASP.NET

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

The ASP Column
Determining Browser Capabilities in ASP.NET
George Shepherd

Contents
HTTP Headers and the User Agent
Determining Browser Capabilities
Mapping Headers to Browser Capabilities
Up-Level Versus Down-Level Browsers
Defining a New Browser
Testing
Creating Your Own Target
HtmlTextWriter and Html32TextWriter
Mobile Browser Capabilities
Conclusion

Web applications are different from applications that run in homogenous environments because they send their output to all kinds of platforms and Web browsers. Some browsers support client-side scripting, some support XHTML, and still others have limited screen real estate. So how will your Web app deliver content to browsers with limited capabilities or special requirements?

To answer that question I'll take an in-depth tour of the ASP.NET 1.1 support for determining browser capabilities. For a look at what's coming up in ASP.NET 2.0, see the sidebar "Browser Capabilities in ASP.NET 2.0." [ Editor's Update - 12/14/2004: The sidebar has been updated.]

HTTP Headers and the User Agent

The first step in determining the capabilities of the browser making a request to your Web site is to find out what kind of browser it is. Inside the payload sent in the HTTP Headers is the User Agent string, which describes the browser making the request. While you don't normally see the headers sent by your client browser to a remote Web site, you can view them using TCP tracing applications. I use TCPTrace (an application written by Simon Fell and Matt Humphrey) to examine headers. Figure 1 shows the header dump for one such Web request.

Figure 1 HTTP Request/Response Payloads in TCP Trace (Close Figure 1)

Here's the text from the upper-right pane of the bitmap:
GET /BrowserCapabilitySite/WebForm1.aspx HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint,
  application/vnd.ms-excel, application/msword, application/x-shockwave-flash, */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0;
  .NET CLR 1.0.3705; .NET CLR 1.1.4322)
Host: localhost:8080
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=cuqtb32q0i2slj45ygahvg45
Extension: Security/Remote-Passphrase
Here's the text from the lower-right pane:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Sat, 30 Oct 2004 17:20:54 GMT
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 1974

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
 <HEAD>
  <title>WebForm1</title>
  <meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
  <meta content="C#" name="CODE_LANGUAGE">
  <meta content="JavaScript" name="vs_defaultClientScript">
  <meta content="http://schemas.microsoft.com/intellisense/ie5"
    name="vs_targetSchema">
 </HEAD>
 <body MS_POSITIONING="GridLayout">
  <form name="Form1" method="post" action="WebForm1.aspx" id="Form1">
<input type="hidden" name="__VIEWSTATE"
  value="dDwxNjEyOTY2OTA2Ozs+LRBIlHeN0PLAvIp7Ptkm85kFdl4=" />
 •••

Notice the User Agent strings in Figure 2. In your ASP.NET application, you can easily fetch the string from the header collection programmatically. You can also look at the User Agent string when you turn tracing on in your application, as shown in Figure 3.

Figure 2 Common User Agent Strings (Close Figure 2)

Browser | User Agent String
Microsoft Internet Explorer 6.0 running on Windows 2000 with the .NET Framework 1.0 and 1.1 installed | Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; CLR 1.1.4322)
Firefox 0.8 running on Windows 2000 | Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.6) Gecko/20040206 Firefox/0.8
Netscape 7.2 running on Windows XP | Mozilla/5.0 (Windows; U; Windows NT 5.1; en-us; rv:1.7.2) Gecko/20040804 Netscape/7.2
Opera 7.54 running on Windows XP | Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) Opera 7.54
Figure 3 The HTTP_USER_AGENT String as It Appears
Figure 3 The HTTP_USER_AGENT String as It Appears

That's great, but what do you do with that information? It's only useful if you know what capabilities are attached to each browser version. In both classic ASP and ASP.NET, the capabilities of various browsers are captured in secondary files.

Determining Browser Capabilities

In classic ASP the DLL %windir%\System32\inetsrv\browscap.dll includes a class that determines browser capabilities based on the incoming User Agent string. The browser capabilities object examines %windir%\System32\inetsrv\browscap.ini to match the User Agent string to a specific section in the .ini file, which describes the well-known capabilities of that browser. (ATL Server, which is a C++ template library for creating ISAPI DLLs, also uses browscap.ini to determine browser capabilities. For more information on ATL Server, take a look at The ASP Column in the November 2003 issue of MSDN®Magazine.)

Figure 4 shows a section from the browscap.ini file reporting the capabilities of a browser identifying itself as Microsoft® Internet Explorer 2.0. This section of the .ini file indicates that Internet Explorer does not support client-side scripting, nor does it support Java applets.

Figure 4 Browscap.ini Section (Close Figure 4)
[IE 2.0]
browser=IE
version=2.0
majorver=2
minorver=0
frames=FALSE
tables=TRUE
cookies=TRUE
backgroundsounds=TRUE
vbscript=FALSE
javascript=FALSE
javaapplets=FALSE
beta=False
Win16=False

To use this information in a classic ASP application, you'd create the BrowserType object in a script block (it's a COM object with the ProgID of "MSWC.BrowserType") and query its properties for information such as browser name, version, and whether cookies are supported. With that information, you can deliver your content appropriately.

ASP.NET includes the HttpBrowserCapabilities class for interrogating the browser's capabilities. The HttpRequest class includes a reference to an instance of the HttpBrowserCapabilties class, so it's always available throughout your application. You can fetch it from the current Page instance or from the current instance of the HttpContext class. HttpBrowserCapabilities includes a number of properties and their significance, as you can see in Figure 5.

Figure 5 Browser Capabilities Represented (Close Figure 5)

Property/Method | Type | Purpose
ActiveXControls | Bool | Support for ActiveX controls
AOL | Bool | America Online browser
BackgroundSounds | Bool | Support for background sounds
Beta | Bool | Beta version
Browser | String | Name of the browser
CDF | Bool | Support for Channel Definition Format (CDF) for webcasting
ClrVersion | Version | Version number of the latest .NET Framework installed on the client
Cookies | Bool | Support for cookies (though not whether cookies are disabled)
Crawler | Bool | Client is a search engine Web crawler
EcmaScriptVersion | Version | Version number of ECMA Script supported by the client
Frames | Bool | Support for frames
JavaApplets | Bool | Support for Java applets
JavaScript | Bool | Support for JavaScript blocks
MajorVersion | Integer | Major version of the browser
MinorVersion | Integer | Minor version of the browser
MSDomVersion | Version | Version of the Microsoft HTML (MSHTML) Document Object Model (DOM) is supported by the client
Platform | String | Operating system running the browser
Tables | Bool | Support for tables
Type | String | Browser name and major version number
VBScript | Bool | Support for VBScript
Version | String | Full version number of the browser
W3CdomVersion | Version | Version of W3C XML DOM supported by the browser
Win16 | Bool | Running on Win16?
Win32 | Bool | Running on Win32?

The significance of most of the browser capabilities is obvious, and you can use the information provided to tailor your output. For example, if you have a script, you should send it to the client only if the browser on the other end understands it, which I demonstrate in the code in Figure 6.

Figure 6 Script Test (Close Figure 6)
protected override void OnInit(EventArgs ea)
{
  string strClientSideScript =
  @"
  <script langage="javascript">
    function DoClick()
    {
      document.all['" + button1.ClientID + @"'].value='Button1 clicked';
    }
  </script>
  ";

  if (HttpContext.Current.Request.Browser.JavaScript)
  {
    Page.RegisterClientScriptBlock("ClientCode", strClientSideScript);
  }
}

Mapping Headers to Browser Capabilities

In the September 2004 installment of this column, I looked at ASP.NET configuration and how ASP.NET has specific handlers for interpreting individual sections within a configuration file. Let's now take a look at parts of the Machine.config file, which is the starting point for configuration in ASP.NET. Near the top of the file you'll see a node providing the name of the component that interprets a User Agent string to determine a browser's capabilities:

<sectionGroup name="system.web">
  <section name="browserCaps"
    type="System.Web.Configuration.HttpCapabilitiesSectionHandler,
      System.Web, Version=1.0.5000.0, Culture=neutral,
      PublicKeyToken=b03f5f7f11d50a3a"/>
  ...
</sectionGroup>

As you can see, the type named "HttpCapabilitiesSectionHandler" is used to parse the <browserCaps> section of the configuration file. After the browser configuration settings have been parsed, for each request it looks at the HTTP headers and uses them to determine the client browser capabilities. The <browserCaps> element within Machine.config contains mappings between HTTP headers and specific browser capabilities. This information is used to populate the HttpBrowserCapabilities class for each request. Note that the browser capabilities feature matches more than just the User Agent string. Any inbound header can be matched and utilized as part of the values set for that HttpBrowserCapabilities object. Figure 7 shows some code from the <browserCaps> section of Machine.config.

Figure 7 <browserCaps> (Close Figure 7)
<system.web>
  <browserCaps>
    <result type="System.Web.Mobile.MobileCapabilities,
       System.Web.Mobile, Version=1.0.5000.0, Culture=neutral,
       PublicKeyToken=b03f5f7f11d50a3a" />

    <use var="HTTP_USER_AGENT"/>

      browser=Unknown
      version=0.0
      majorversion=0
      minorversion=0
      frames=false
      tables=false
      ...

  </browserCaps>
</system.web>

The <browserCaps> section defines a set of key-value pairs representing capabilities. Further down in the Machine.config file, you'll see mappings of these variables to specific regular expressions in various User Agent strings. For example, between the <browserCaps> tags, you'll see regular expressions indicating the client version. If the HttpBrowserCapabilitiesHandler matches the platform strings in the User Agent string, HttpBrowserCapabilitiesHandler sets the platform property appropriately:

<case match="Windows NT 5.1|Windows XP">
  platform=WinXP
</case>
<case match="Windows NT 5.0|Windows 2000">
  platform=Win2000
</case>

Up-Level Versus Down-Level Browsers

Browsers as defined in Machine.config can be categorized into two main types: up-level and down-level. Up-level browsers support HTML 4.0, cascading style sheets, ECMAScript version 1.2 (JScript® and JavaScript), and a W3C-compatible Document Object Model. Down-level browsers and client devices support only HTML 3.2. The distinction between browsers is useful for evaluating how the appearance of your Web site will change when accessed in the most common use cases.

Most modern browsers are mentioned within Machine.config. If a browser is not defined there, you can easily add a definition for one to the Machine.config or to your application's Web.config.

Defining a New Browser

Just as nearly all other configuration settings mentioned within Machine.config are malleable, so are the browser capability settings. Imagine there's an esoteric browser for which you need to custom-tailor the output of your site. To support the browser capabilities for the new browser, set up a separate <browserCaps> section within your Web.config file, like you see in Figure 8.Browser Capabilities in ASP.NET 2.0 and Matt Gibbs

ASP.NET 1.1 contains configuration data for a much larger set of browsers than ever before, including many browsers found on mobile devices. And since the release of ASP.NET 1.1, Microsoft has released four Device Updates that added rendering support and configuration data for even more browsers. An updated browserCaps section now contains many complex regular expressions, nested elements matching various HTTP headers, and property sets that override some already set by earlier matches. This complex configuration section is being overhauled in ASP.NET 2.0 to be simpler to understand, easier to maintain, and to have better performance.

The new version of browserCaps relies on a tree structure of browser definitions in XML files. The definitions are held in files with the intuitive extension of ".browser". As of the Beta 1 release, the machine-wide files are in the browsers subdirectory of the .NET Framework configuration directory, and application-specific files can be placed in a directory under the Web application root with the same name.

One of the key changes in this version of the browserCaps feature is that the .browser files are used to generate code for a factory type that, given an HttpRequest, will produce an HttpBrowserCapabilities object. In the previous version, the regular expressions were processed in the order that they were parsed from the configuration files. The organization was less structured than it is now, and there was no corresponding compiled factory type.

The machine-wide factory is produced by explicitly running the new aspnet_regbrowsers tool. Thus, ASP.NET does not have to continuously monitor the global .browser files. ASP.NET will not automatically restart all of the Web applications for each and every change. After all, global changes to the configuration typically occur infrequently. The application-level browser files are automatically processed into an object that derives from the global type and does not require the use of a separate tool. Changes at that level are more common, so they are automatically detected, resulting in the generation of a new type that inherits from the global factory type. Because the global type is shared across all applications, there is an inherent advantage to this inheritance model along with the need to regenerate the application-level factories as needed. The application-level type derives from the global type and allows you to easily override and add to the global browser definitions.

There are two main elements that can make up a .browser file: browser elements and gateway elements. Starting at the root node, the generated code does the equivalent of a tree walk, trying to match the regular expressions identifying an element against the request. At any given node, only one child browser element can match or it is considered a runtime error while multiple gateway matches are permitted. [ Editor's Update - 12/14/2004: At any given node, only one child browser element can match, or the runtime will stop the tree traversal at that point. Multiple gateway matches are permitted.] This restriction eliminates some of the previous ambiguity found when working to update the browserCaps section. The regular expressions must provide unique matches so that a definitive path through the tree can be found. A request does not necessarily need to match all the way to a leaf node. The resulting HttpBrowserCapabilities object is the result of the default settings specified in the root node and the cumulative effect of all the additional properties and overridden settings that are applied as the tree is traversed.

A browser element can either create a new node in the tree by specifying a parent browser node, or it can modify the settings for an existing node by referencing it. If a browser element is not modifying an existing node, it will include an identification section to indicate what capabilities or header values must exist for a browser match to be true. In addition to identifying a browser, the node can also contain a set of elements that capture data from the headers of the request. This information can then be used in the capabilities section to set properties of the HttpBrowserCapabilities object.

Another change to the browser capabilities system is the support for specifying control adapters. ASP.NET 2.0 features a pluggable control adapter architecture that allows you to take over or participate in many of the lifecycle behaviors of the controls in a page. The need to adapt a control's behavior or output is typically a function of the browser that is being targeted, so it makes sense to include the controlAdapters element in the browser definition as well.

A final key feature of the new browser capabilities system is how it works to provide support for browser-specific page customization. In version 2.0 of ASP.NET, you can customize property values declaratively based on the requesting browser. Simply prefix a control attribute with the ID of a browser. The most specific match from the tree traversal will be used as the final value of the property. For example, a label can declaratively set different text to be used based on what browser has made the request, as shown here:

<asp:label runat="server" text="some browser" IE:text="any IE browser"
  IE5:text="the IE 5 browser"  PIE:text="the Pocket PC browser" />
This makes it easy to tailor applications for the unique capabilities of the various browsers.

The enhancements to the browser capabilities feature in ASP.NET 2.0 should make it easier for you to keep up with the changing features of new browsers as they emerge to customize applications for varying browsers, and even to modify control behaviors through the use of adapters.

Figure 8 Web.Config (Close Figure 8)
<system.web>
  <browserCaps>
    <filter>
      <case match="LameBrowser">
        browser=Lame-o-browser
        version=0.0
        majorversion=0
        minorversion=0
        frames=false
        tables=false
        cookies=false
        backgroundsounds=true
        vbscript=false
        tagwriter=System.Web.UI.Html32TextWriter
        ...
      </case>
    </filter>
  </browserCaps>
</system.web>

This tells ASP.NET about a new kind of browser named the "LameBrowser". Given the definition in the <browserCaps> section, this is a down-level browser that supports background sounds. If the User Agent includes the string "LameBrowser", ASP.NET will fill the current HttpBrowserCapabilities object with the values mentioned in Web.config. Note, though, that the matching of the regular expressions is additive, so a subsequent match further down in the application hierarchy could override these settings.

Testing

If you have copies of your target browsers in your test environment, it's pretty easy to see how your Web site will respond. If not, ASP.NET supports an aliasing technique that lets you force your application into thinking a certain browser made a request.

In Machine.config, you'll find a <clientTarget> section that defines aliases for Internet Explorer 4.0 and 5.0, up-level browsers, and down-level browsers:

<clientTarget>
  <add alias="ie5" userAgent=
    "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0)"/>
  <add alias="ie4" userAgent=
    "Mozilla/4.0 (compatible; MSIE 4.0; Windows NT 4.0)"/>
  <add alias="uplevel" userAgent=
    "Mozilla/4.0 (compatible; MSIE 4.0; Windows NT 4.0)"/>
  <add alias="downlevel" userAgent="Unknown"/>
</clientTarget>

As you can see, the <clientTarget> section maps short names to specific User Agent strings. If you set your page's ClientTarget property to any of the aliases listed, the ClientTargetSectionHandler class will intercept the alias and use the specific User Agent string to determine the browser's capabilities. Here's an example of setting ClientTarget within the page directive (you can also set this property in the Visual Studio designer):

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs"
    Inherits="BrowserCapabilitySite.WebForm1" clientTarget="uplevel"%>
This allows you to render your page render in different browsers.

Creating Your Own Target

While ASP.NET gives you four built-in target aliases, you probably could benefit from more. For example, imagine you wanted to test your site against the Firefox browser or the LameBrowser mentioned previously. The following bit of XML placed in your Web.config file will do the trick (provided you have <browserCaps> sections set up for each of them):

<system.web>
  <clientTarget>
    <add alias="firefox" userAgent="Mozilla/5.0 (Windows; U;
      Windows NT 5.0; en-US; rv:1.6) Gecko/20040206 Firefox/0.8" />
    <add alias="lamebrowser" userAgent="LameBrowser"/>
 </clientTarget>
<system.web>

HtmlTextWriter and Html32TextWriter

In addition to the properties shown in Figure 5, HttpBrowserCapabilities exposes another read-only property named TagWriter. The TagWriter property exposes the Type of the class to use to render tags from a control. If you've ever written a custom server-side control, you've undoubtedly seen the HtmlTextWriter class passed into the Render method.

You might think calling output.Write from within a server-side control is the same as calling Response.Write, but it's actually more flexible than that. The HtmlTextWriter contains methods for rendering specific HTML elements.

While it's often good enough to contain other ASP.NET server-side controls when writing a custom control, you sometimes need to work with tags manually. The HtmlTextWriter class includes methods to support stack-based rendering of tags and attributes.

In addition to helping you keep beginning and ending tags straight, the HtmlTextWriter helps you emit the right version of HTML. When ASP.NET gets a request, it examines the TagWriter property of the HttpBrowserCapabilities class and uses either HtmlTextWriter or Html32TextWriter (Html32TextWriter derives from HtmlTextWriter). For example, tables render slightly differently in HTML 3.2 than they do in HTML 4.0. The code snippet in Figure 9 (showing the Render method overridden in a server-side control) renders a simple table with one row and one column. HtmlTextWriter includes methods to render the attributes and tags based on the HTML version understood by the browser.

Figure 9 Rendering a Control (Close Figure 9)
protected override void Render(HtmlTextWriter output)
{
  output.WriteFullBeginTag("h2");
  output.Write("Using the HtmlTextWriterClass ");
  output.Write(output.ToString());
  output.WriteEndTag("h2");
  output.WriteLine();

  output.AddAttribute(HtmlTextWriterAttribute.Width, "30%");
  output.AddAttribute(HtmlTextWriterAttribute.Border, "2");
  output.RenderBeginTag(HtmlTextWriterTag.Table);
  output.RenderBeginTag(HtmlTextWriterTag.Tr);
  output.AddAttribute(HtmlTextWriterAttribute.Align, "Right");
  output.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "large");
  output.AddStyleAttribute(HtmlTextWriterStyle.Color, "red");
  output.RenderBeginTag(HtmlTextWriterTag.Td);
  output.Write("Hi there...");
  output.RenderEndTag();
  output.RenderEndTag();
  output.RenderEndTag();
}

Here's how the Html32TextWriter renders the table

<table width="30%" border="2">
  <tr>
    <td align="Right">
      <font color="red" size="5">
      Hi there...
      </font>
    </td>
  </tr>
</table>
while the HtmlTextWriter class renders the table like this:
<table width="30%" border="2">
  <tr>
    <td align="Right" style="font-size:large;color:red;">
    Hi there...
    </td>
  </tr>
</table>
Remember, Page derives from the Control class and you may override the Render method to access the HtmlTextWriter from a Page. The ASP.NET standard server-side controls are built to render based upon the capabilities of the browser making the request.

Mobile Browser Capabilities

So far, I've looked only at the basic set of browser capabilities. A whole new set of mobile browsers are supported as of ASP.NET 1.1. A tour through the Machine.config file of an ASP.NET 1.1 installation reveals settings for devices and systems such as Windows® CE, Nokia, Ericsson, and AvantGo. These are the browsers you'll find built into modern cell phones and PDAs.

ASP.NET determines these capabilities in exactly the same way it does for normal browsers. However, as you look through the various devices, you'll see that determining browser capabilities for these devices is a bit more involved because there are just a lot more capabilities to figure out.

Determining the mobile capabilities of a device is a mere type cast away from the normal device capabilities. That is, just cast the Request.Browser field to the MobileCapabilities class and you can examine the device capabilities (MobileCapabilities derives from HttpBrowserCapabilities). The code in Figure 10 determines the nature of the markup language of the browser/device (WML versus HTML), the size of the user's screen (in characters), and whether or not the device can deal with e-mail.

Figure 10 Determining Mobile Browser Capabilities (Close Figure 10)
protected void ShowMobileCapabilities()
{
  MobileCapabilities mobileCapabilities =
    (MobileCapabilities)Request.Browser;

  if(mobileCapabilities.PreferredRenderingMime == "text/html")
  {
    Response.Write("This is an HTML device.");
  }
  else if(mobileCapabilities.PreferredRenderingMime ==
    "text/vnd.wap.wml")
  {
    Response.Write("This is a WML device.");
  }
  Response.Write("Screen Width in characters: " +
    mobileCapabilities.ScreenCharactersWidth);

  Response.Write("Screen Height in characters: " +
    mobileCapabilities.ScreenCharactersHeight);

  Response.Write("Can send mail: " + mobileCapabilities.CanSendMail);
}

When you look at Machine.config you'll see more than 75 mobile device capabilities (in addition to the standard browser capabilities). The mobile capabilities of a device are determined by the MobileDeviceCapabilitiesSectionHandler configuration handler.

Conclusion

ASP.NET provides a number of features for determining the capabilities of the client making any given request. This provides you with a means of controlling your site's output by aliasing client targets to specific User Agent strings within your application.

Send your questions and comments to asp-net@microsoft.com.

George Shepherd specializes in software development for the .NET Framework. He is the author of Programming with Microsoft Visual C++.NET (Microsoft Press, 2002), and coauthor of Applied .NET (Addison-Wesley, 2001). He is a contributing architect for Syncfusion's Windows Forms and ASP.NET tools.


Security Briefs: Security Enhancements in the .NET Framework 2.0

[ Editor's Update - 1/20/2006: This article refers to a beta version of Visual Studio 2005. An updated version of the article, reflecting features found in the final release of Visual Studio 2005, can be found at Security Briefs: Security Enhancements in the .NET Framework 2.0.]
Security Briefs
Security Enhancements in the .NET Framework 2.0
Keith Brown

Contents
System.Security
System.Security.Principal
System.Security.AccessControl
System.Net.Security
System.Runtime.Remoting
System.Security.Cryptography
System.Security.Cryptography.X509Certificates and PKCS
System.Security.Cryptography.Xml
System.Diagnostics
System.DirectoryServices
System.DirectoryServices.ActiveDirectory
System.Web
Changes in Code Access Security
Wrapping it Up

As I write this column, version 2.0 of the Microsoft® .NET Framework is at Beta 1. When I got my bits, I hacked together a little program to dump all of the public members of all public types in the entire Framework and ran it on version 1.1 as well as 2.0. I then used WINDIFF.EXE to compare the two text files, and spent a few hours paging through the changes taking notes, paying special attention to anything that was security related.

Security support in the .NET Framework got a lot of love in version 2.0, and this month I'm going to take you on a whirlwind tour of the goodies you'll find there. I won't be able to cover everything, but you'll know where to start looking to stay on top of the new changes. I'll take this a namespace at a time. Oh, and the obvious caveat applies: this is beta software, so anything I talk about here could change before the final release.

System.Security

Managed strings are a bad storage medium for secrets. There's really no way to erase one. Garbage collection doesn't make any guarantees about overwriting collected memory, and in many cases, strings that become garbage remain in the managed heap for some time before being overwritten by a heap compaction. Moreover, the garbage collector might move them around in the managed heap. Combine that with the fact that it doesn't zero out old memory, and you may end up with many instances of the string you're trying to keep secret in your address space. A new class, SecureString, has been introduced to help alleviate these problems.

This class stores its data using the Data Protection API (DPAPI) protected memory model. In other words, data is always in its encrypted form while it is stored inside of a SecureString. The encryption key is managed by the local security authority subsystem (LSASS.EXE) and through the DPAPI, the data can be decrypted via interprocess communication.

In order for this class to be effective, any .NET Framework method that reads secret data (perhaps a password from the console, perhaps a connection string from Web.config) needs to directly encapsulate that data in a SecureString and return it to you. You can't simply wrap a SecureString around a normal string and expect to be any safer—to be effective, the secret must never ever find its way into a normal managed string.

The same rule applies for giving secrets back to the .NET Framework. For example, the new Password property in ProcessStartInfo (I'll get to this a bit later) is of type SecureString. Under the covers, the .NET Framework will use the Marshal class to retrieve the actual contents of the secret data when it is needed via new functions such as Marshal.SecureStringToBSTR. Because the Marshal class deals in unmanaged memory, that memory can be properly zeroed out when the Framework has finished using it. And no, you cannot use ToString to retrieve the secret from SecureString. Remember, you never want to end up with secret data in a normal string object.

One thing I don't see, however, is a way to keep the secret out of the swap file. When the secret data is unmarshaled into native memory, I see no way to ensure that the page to which it is unmarshaled is locked via VirtualLock.

The other thing I don't see in Beta 1 is broad integration of SecureString into the Framework. So far the only place I see it being used is ProcessStartInfo. Until this class is integrated more fully into the Framework, its real power won't be realized.

For example, I'd like to see the following constructor:

public SqlConnection(SecureString connStr);
I'd also like to see a managed class that wraps Win32® functions like CredUIPromptForCredentials, so I can ask the user for a password and get back a SecureString as a result. Integration will be the key to the success of this feature.

Besides SecureString, there is another new class in this namespace that caught my eye: SecurityContext. There's even some documentation for this class in Beta 1, and whoa is this ever a powerful class! It allows you to capture the security context of a thread and restore it on another thread. This includes the Code Access Security (CAS) thread markers such as Assert and PermitOnly, and it also includes the impersonation token for the unmanaged thread.

There are some wild looking functions on this class, such as SuppressFlow, which controls whether CAS markers are flowed from one thread to another (they normally are). And then there was the shocker: SuppressFlowWindowsIdentity. Anybody who has been doing security programming in Windows® for long knows that when you kick off a new thread or perform an asynchronous call via a delegate, the impersonation token doesn't flow to the new thread. This little security gotcha has bitten more than a few developers over the years. Well, if SecurityContext is offering a function to suppress the flow of a WindowsIdentity across threads, that must mean that the .NET Framework is now flowing impersonation tokens automatically.

To test this, I hacked up some code that called LogonUser and impersonated the resulting token. I then used a nifty new overload of the GetCurrent method on WindowsIdentity to determine if I was impersonating or not (more on this in a moment). I called this function from four different places: the main thread, a new thread kicked off via Thread.Start, a worker thread via an asynchronous delegate, and a thread kicked off by our good old friend from Win32, CreateThread. Oh, I even tried testing QueueUserWorkItem, which in the past has behaved a bit differently toward flowing security context (specifically, CAS markers). Here's the output:

Main thread
Thread 2488 is impersonating V-CAMP-XP\alice
Testing Thread.Start
Thread 2508 is impersonating V-CAMP-XP\alice
Testing QueueUserWorkItem
Thread 2504 is impersonating V-CAMP-XP\alice
Testing Delegate.BeginInvoke
Thread 2516 is impersonating V-CAMP-XP\alice
Testing Native CreateThread
Thread 2520 is not impersonating!

Wow! So the runtime is indeed propagating impersonation tokens across threads automatically now, and if you want to change this behavior, you can use the SecurityContext class (though I don't recommend doing this).

Note that the thread created by the unmanaged API CreateThread isn't covered, but that's to be expected because it's the runtime, not the operating system, that's doing this work. This means that calls to inproc COM components that generate thread switches due to threading model mismatches will still drop the impersonation token just like before, but for a large class of applications, the cross-thread identity flow problem has been solved. This is great news, because it will lead to less surprising results for most people. This is an important and subtle change, so be prepared for it if you are using impersonation and asynchrony in your application.

System.Security.Principal

It may seem like a small thing, but the new class that models a security identifier (SID) paves the way for broad support of the Windows security programming interface. I'm going to be spending much less time writing managed C++ to bridge the gaps, and that makes me a very happy camper. There are three classes that make this possible. The first two are concrete classes that represent the two means by which you can reference a security account (either a user, group, or computer): SecurityIdentifier, which represents the machine-readable SID, and NTAccount, which stores the human readable string. The third, an abstract class called IdentityReference, binds these together.

A method that would normally return a SID, a collection of SIDs, or a collection of data structures that contain SIDs, will use the abstract class in its signature. The method will take an extra Type argument to allow you to choose whether you want the method to return SIDs or names. You can then safely downcast to the appropriate type. Here's an example that displays the account name of the user that owns a file:

void printTheOwnerOfThisFile(string path) {
  FileSecurity s = File.GetAccessControl(path);
  NTAccount user = (NTAccount)s.GetOwner(typeof(NTAccount));
  Console.WriteLine(user);
}
...
printTheOwnerOfThisFile(@"c:\autoexec.bat");
Running that code on my system prints the following string:
BUILTIN\Administrators

There is also a complete listing of well-known SIDs in the new enumeration called WellKnownSidType. This makes it trivial to construct SIDs for well-known accounts without hardcoding strings like "Administrators", which isn't spelled the same in Germany as it is in the United States.

The WindowsIdentity class got some attention as well. As mentioned previously, one of my favorite additions is an overload of GetCurrent that allows you to distinguish between the thread and process security context during impersonation:

bool ifImpersonating = true;
WindowsIdentity threadIdentity =
  WindowsIdentity.GetCurrent(ifImpersonating);
if (null == threadIdentity) Console.WriteLine("Not impersonating");
else Console.WriteLine("Impersonating {0}",threadIdentity.Name);

System.Security.AccessControl

This is big. The Framework now models Windows security descriptors, allowing you to programmatically read and modify access control lists (ACL), take ownership of objects, convert between security descriptor description language (SDDL) strings and binary security descriptors, and more. I've watched the design of this namespace progress from the Alpha pre-release, and I must say, the work that went into it after the Alpha has made this a really powerful and easy-to-use addition.

You already saw a simple example of this new feature when I printed out the owner of a file. Notice how I was able to access the file's security descriptor via the System.IO.File class. You'll find that throughout the Framework, secure objects have sprouted two new methods: GetAccessControl and SetAccessControl. Classes like AutoResetEvent, Mutex, and the new Semaphore represent three such objects, as do the more obvious File and Directory classes.

There's no direct way of changing the discretionary access control list (DACL) for a service. There is simply no built-in managed representation of a service object right now. But after I learned how the access control infrastructure worked, it took me about 15 minutes to put together my own support for service security. The System.Security.AccessControl namespace was designed from the ground up with extensibility in mind. Kudos guys!

For anyone who has ever programmed ACLs before, you'll appreciate the simplicity of the code involved (see Figure 1). For more information on the System.Security.AccessControl namespace, see Mark Novak's article in the November 2004 issue of MSDN®Magazine, available at Manage Access to Windows Objects with ACLs and the .NET Framework.

Figure 1 Programming ACLs (Close Figure 1)
FileSecurity sd = new FileSecurity();

// block inherited ACEs
sd.SetAccessRuleProtection(true, false);
FileSystemRights read =
    FileSystemRights.ReadPermissions |
    FileSystemRights.ReadData |
    FileSystemRights.ReadAttributes |
    FileSystemRights.ReadExtendedAttributes;

sd.AddAccessRule(new FileSystemAccessRule(
    new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid,
        null), FileSystemRights.FullControl, AccessControlType.Allow));

sd.AddAccessRule(new FileSystemAccessRule(
  new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null),
      read, AccessControlType.Allow));

File.SetAccessControl(@"c:\work\test.txt", sd);

System.Net.Security

One of my favorite topics for discussion is authentication protocols, but programming against them has always been just a little bit tricky. Adding Kerberos to an application used to involve calling some low-level functions in the Security Support Provider Interface (SSPI), and adding Secure Sockets Layer (SSL) support was even more difficult.

Thankfully there are now managed wrappers for SSPI that allow you to implement both client and server-side secure channels using Kerberos or SSL. Kerberos support is brought to you by NegotiateStream, which technically negotiates between Kerberos and an older challenge-response protocol called NTLM. The client-side handshake can be either synchronous or asynchronous. Here's an example of synchronous setup for a client:

NegotiateStream secureChannel = new NegotiateStream(networkStream);
secureChannel.ClientAuthenticate(
    CredentialCache.DefaultNetworkCredentials,
    "SSPISample/TargetMachine:4242", ProtectionLevel.EncryptAndSign,
    TokenImpersonationLevel.Impersonation);

After doing that, you can use secureChannel just like you would a normal NetworkStream, but any data you push through secureChannel will be integrity-protected via a message authentication code (MAC) and encrypted. You'll need an instance of NegotiateStream on the server side as well in order to decrypt the incoming messages, and, of course, the server can obtain a WindowsIdentity representing his client via the RemoteIdentity property. From there the server can impersonate or perform role-based access checks.

SslStream looks very similar, but of course uses X.509 certificates as opposed to Kerberos tickets during authentication. It allows the client to check for server certificate revocation and provide a client certificate for mutual authentication. In the same way, once the authentication handshake is finished, you can push and pull data through the pipe and rest assured that it's being integrity-protected and encrypted.

System.Runtime.Remoting

Another exciting development is that the TCP channel now supports security. This is built into the channel using the NegotiateStream class I talked about earlier, and can be activated by specifying a few new attributes in the client and server remoting configuration files. I don't have room in this column to drill down into this topic, but there's a chapter covering this in my book, which you can read online at The .NET Developer's Guide to Windows Security.

System.Security.Cryptography

DPAPI is now a first-class citizen in the .NET Framework, and two .NETclasses, ProtectedData and ProtectedMemory, make it a breeze to use:

private static byte[] Decrypt(byte[] ciphertext) {
    byte[] applicationEntropy = Encoding.ASCII.GetBytes("AcmeWidgets");
    return ProtectedData.Unprotect(ciphertext, applicationEntropy,
        DataProtectionScope.LocalMachine);
}

For those of you building cryptosystems using the .NET Framework, you'll be happy to know that there's an entirely new family of classes that derive from the new abstract class, keyed-hash message authentication code (HMAC). All the wide SHA variants are covered. Here's an example:

byte[] CalcMAC(byte[] key, byte[] msg) {
    return new HMACSHA256(key).ComputeHash(msg);
}

If you're doing password-based cryptography, generating key material from passwords or pass phrases, you can now use the industry-standard algorithm for generating a byte stream from a password, PKCS#5 (documented in RFC 2898). The new Rfc2898DeriveBytes makes it easy to salt and stretch passwords, as I discussed in my last column. Under the covers, it's using the PBKDF2 function with HMAC-SHA-1:

void CreateNewUserAccount(string name, string pwd) {
    Rfc2898DeriveBytes db = new Rfc2898DeriveBytes(password, 32, 1000);
    byte[] hash = db.GetBytes(32);
    addUserAccountToDatabase(name, db.Salt, hash);
}

System.Security.Cryptography.X509Certificates and PKCS

No more CAPICOM! With these new namespaces you've now got managed wrappers for CryptoAPI (CAPI) that allow you to manage certificates and create CMS/PKCS#7 enveloped or signed messages directly from any managed language.

Need to ask the user to choose a certificate from her personal store? It's pretty natural now:

X509Store store = new X509Store("My", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509CertificateExCollection certs =
    store.Certificates.Select( "MyTitle", "MyMessage",
        X509SelectionFlag.SingleSelection);
if (1 == certs.Count) {
    Console.WriteLine("You selected \"{0}\"",
        certs[0].SubjectName.Name);
}
else Console.WriteLine("You canceled my dialog!");

Figure 2 shows the resulting dialog. If you were paying attention to that code, you may have noticed that the .NET Framework team has gone down the slippery slope of naming a class with an "Ex" suffix. Wasn't it Mr. Bunny's Guide to ActiveX® controls that taught us not to name stuff this way in COM? Just wait, I'll bet in a few years you'll see an Ex2 version.

Figure 2 Choosing a Certificate
Figure 2 Choosing a Certificate

System.Security.Cryptography.Xml

This namespace has been fleshed out with support for XML Encryption. For information on using XML Signature and XML Encryption to exchange data securely, see Mike Downen and Shawn Farkas's article in the November 2004 issue of MSDN Magazine at Exchange Data More Securely with XML Signatures and Encryption.

System.Diagnostics

Besides a whole boatload of new tracing and debugging features, better event log support and even a Stopwatch, there's a hidden gem lurking inside of the Process class: the ability to invoke CreateProcessWithLogonW without having to explicitly use P/Invoke. This is the "run as" feature in Windows that developers use all the time to run programs using alternate credentials when testing. Now it's easy to use programmatically from managed code:

void RunNotepadAsThisUser(string authority,
    string principal, SecureString password)
{
    Process.Start(@"c:\windows\notepad.exe",
        principal, password, authority).Dispose();
}

Note the use of SecureString—that's the only trick here because where do you get one of those? One way would be to use one of the Win32 credential APIs such as CredUIPromptForCredentials to ask the user for a password, and then stuff the unmanaged string into a SecureString instance that you can pass to the Process.Start method. Then you would carefully zero out the buffer that held the password. I'm looking forward to having the credentials API wrapped in the .NET Framework. Maybe we'll get lucky and see it emerge in the next Beta.

System.DirectoryServices

The biggest change here seems to be the addition of support for the Virtual List View (VLV). This Windows Server™ 2003 feature allows you to perform very large searches and scroll through them efficiently. You don't have to retrieve the entire resultset at once. With the VLV, you can specify a window of results that you want, and then scroll the window. Here's an example that searches for all users in the forest, but only returns the first 25 records:

DirectorySearcher search = new DirectorySearcher(
    "(&(objectClass=user)" + "(objectCategory=person))");
search.VirtualListView = new DirectoryVirtualListView(25);
foreach (SearchResult result in search.FindAll()) {
    Console.WriteLine(result.Path);
}

Once you create a VLV and attach it to a searcher, you can control the window of displayed results by setting properties like Offset (which is the index into the resultset by record number), TargetPercentage (the index into the resultset by a percentage of the number of records), or Target (the index into the resultset via a string that the desired result should start with).

System.DirectoryServices.ActiveDirectory

This is a new namespace that makes it easy to view and manipulate the physical and logical configuration of Active Directory®. Here are some of the class names to give you an idea of what's modeled: Forest, Site, Domain, DomainController, GlobalCatalog, and there are many more. Also provided here is a set of classes for reading and manipulating the Active Directory schema. I could spend an entire column talking just about this namespace—there's lots of great stuff here, so be sure to check it out.

System.Web

Back in June 2004, I wrote an article on the new security features in ASP.NET 2.0, so I won't repeat all that stuff here (see Security Headaches? Take ASP.NET 2.0!). But there's one thing that I didn't cover that appears to be fully baked now, and that's configuration file encryption. Wouldn't it be nice to just ask the .NET Framework to use DPAPI to encrypt an entire section of your configuration file, perhaps the AppSettings section? Well, check this out:

// use this app's config file
string configFilePath = null;
Configuration config = Configuration.GetExeConfiguration(path,
    ConfigurationUserLevel.None);
config.AppSettings.ProtectSection(ProtectedConfiguration.
    DataProtectionProviderName);
config.Update();
This turns a config file that started life looking like this
<configuration>
  <appSettings>
    <add key='MySecret' value='Attack at dawn'/>
  </appSettings>
</configuration>
into something that looks like Figure 3 (I've abbreviated the ciphertext to keep things readable).

Figure 3 The Encrypted Text (Close Figure 3)
<configuration>
  <protectedData>
    <protectedDataSections>
      <add name="appSettings"
          provider="DataProtectionConfigurationProvider" />
    </protectedDataSections>
  </protectedData>
  <appSettings>
    <EncryptedData>
      <CipherData>
        <CipherValue>AQAAA...</CipherValue>
      </CipherData>
    </EncryptedData>
  </appSettings>
</configuration>

Now guess how you access the "MySecret" data. That's the cool part—you access it just like you would have before, and the decryption happens behind the scenes:

Console.WriteLine(ConfigurationSettings.AppSettings["MySecret"]);
There's also an RSA provider you can use that relies on a CAPI key container to store the encryption key. You would use this class if you needed to deploy a single config file to many different machines in a Web farm, for example. With CAPI, you can synchronize the encryption key across the entire farm, whereas DPAPI is a machine-specific encryption technique.

Changes in Code Access Security

A whole bunch of stuff has been added to the CAS infrastructure for version 2.0 of the .NET Framework. New extensibility points in the stack-walking mechanism allow permissions to override the handling of demands (look up CodeAccessSecurity.CheckDemand and friends if you're interested in learning more).

A new class of evidence tells you whether an assembly was loaded from the global assembly cache (GAC).

There are several permissions that moderate access to the new features in the .NET Framework 2.0. Take, for example, SmtpPermission which regulates access to the client-side SMTP functionality exposed by the new System.Net.Mail namespace.

One new permission that I think is pretty neat is the DataProtectionPermission, which controls which code is allowed to use the DPAPI classes, ProtectedData and ProtectedMemory. This is really important because you don't want random code that was downloaded from some untrusted source to be allowed to decrypt that config file that we just got so excited about encrypting.

Besides all this, there's a whole new set of classes to deal with ClickOnce deployment and trust determination, but I'll save that for another column.

Wrapping it Up

Version 2.0 of the .NET Framework is a boon for developers who care about security. I've just scratched the surface of the work that this team has done—it's at least as large an effort as what we've seen happening in ASP.NET 2.0, as far as I can see. This is definitely a version I'm looking forward to working with. Have fun experimenting with the Beta!

Send your questions and comments for Keith to briefs@microsoft.com.

Keith Brown is a co-founder of Pluralsight, specializing in developing and delivering high-quality training for software developers. Keith's most recent book, The .NET Developer's Guide to Windows Security, is available here. Keith also keeps a Web log and contact information at www.pluralsight.com/keith.


.NET Matters: Sepia Tone, StringLogicalComparer, and More

.NET Matters
Sepia Tone, StringLogicalComparer, and More
Stephen Toub

Code download available at: NETMatters0501.exe (195KB)

Q My digital camera allows me to take pictures using a "sepia" filter which results in photos that look like they were taken in the late 1800s. While this is cool, I'd rather take the pictures normally and then apply this change later on. I can probably do something like this in Digital Image Pro 10, but I'm working on my own photo utility library in .NET and was hoping to add this functionality. What's the best way to change the coloring of a bitmap?

A There are a few ways to change the colors that make up an image. One approach is to iterate over all of the pixels in the image, retrieving each pixel's color using Bitmap.GetPixel, modifying the color to your liking, and then setting the color back into the image using Bitmap.SetPixel (note that a System.Drawing.Color structure is immutable once created, so you'll actually need to create a new value based on the old one). This is a relatively slow process, however. The conversion can be sped up using unsafe code to access the image data, but that still requires you to manually touch each pixel. Eric Gunnerson describes this process in his article "Unsafe Image Processing".

An easier way is to use the ColorMatrix class, available in the System.Drawing.Imaging namespace. ColorMatrix defines a 5¥5 matrix that contains the coordinates for the RGBA color space (the A stands for alpha, a measure of the transparency of the color) and can be used in coordination with the ImageAttributes class to control how images are drawn onto a Graphics surface. When applied, the ColorMatrix is multiplied by the color vector for each pixel in the image to come up with the new color pixel (for more on this operation, see ColorMatrix Basics - Simple Image Color Adjustment). In addition to being easier, this method is also much faster than manually manipulating each pixel. My informal tests show it to be an order of magnitude faster than the GetPixel/SetPixel approach.

So, in order to create a sepia-colored image from your photos, you can create a ColorMatrix that represents the necessary linear transformation for the color space. Sepia is a tone where the colors of the image appear in shades of brown rather than in shades of gray (or grayscale). With black and white photographic prints, metallic compounds, bleach, and other chemicals are used to turn the black areas to brown, a technique popular a century ago (possibly because skin tones can look more lifelike in sepia tone than in grayscale). Of course, chemicals aren't necessary when dealing with digital images; one plausible matrix can be created as follows:

private ColorMatrix CreateSepiaMatrix() {
    return new ColorMatrix(new float[][]{
        new float[] {0.393f, 0.349f, 0.272f, 0, 0},
        new float[] {0.769f, 0.686f, 0.534f, 0, 0},
        new float[] {0.189f, 0.168f, 0.131f, 0, 0},
        new float[] {     0,      0,      0, 1, 0},
        new float[] {     0,      0,      0, 0, 1}
    });
}
This corresponds to the following color transformation:
New Red   = R*.393 + G*.769 + B*.189
New Green = R*.349 + G*.686 + B*.168
New Blue  = R*.272 + G*.534 + B*.131
Note that these numbers aren't necessarily the "right" answer. This matrix is just one possible solution, and you can play with the numbers in this matrix to better suit your eye and needs. Of course, other shifts are possible by changing the numbers accordingly, such as creating the green and blue hues prevalent throughout the Matrix trilogy (no pun intended).

Once I have the color matrix I want to use, I can set it on an ImageAttributes instance and then use that instance in a call to DrawImage, as shown here:

Image img = GetImage();
ImageAttributes imageAttrs = new ImageAttributes();
imageAttrs.SetColorMatrix(CreateSepiaMatrix());
using(Graphics g = Graphics.FromImage(img))
{
    g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
        0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imageAttrs);
}
Figure 1 shows my original image and then a transformed image that resulted from running this code on the original.

Figure 1 A Duck and Peacock in Original and Sepia Tones
Figure 1 A Duck and Peacock in Original and Sepia Tones

Q I'm working with a list of files and I want to sort this list before displaying it to the user. Many of the files have numbers in the names at multiple locations, for example with a pattern similar to Class#_Student#.txt, and I'd like the sorting algorithm to consider those numbers in their entirety during the sort, rather than sorting based on the character values of the individual digits. So, if I have a list of files named "Class1_Student1.txt", "Class10_Student1.txt", and "Class2_Student1.txt", they should be sorted as "Class1_Student1.txt", "Class2_Student1.txt", and "Class10_Student1.txt". Is there anything in the .NET Framework that will allow me to easily perform this form of sort?

A As far as I know, the base class libraries do not provide a string comparison option that would allow for this kind of comparison. However, given that you're asking about doing this with file names, you probably already know that the Windows® shell is capable of sorting files by name in this fashion. In fact, the Microsoft® Shell Lightweight Utility Library, shlwapi.dll, exposes this comparison routine as StrCmpLogicalW. You can easily consume this function from your managed code using P/Invoke:

[DllImport("shlwapi.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
private static extern int StrCmpLogicalW(string strA, string strB);
It's then very straightforward to incorporate this method into an IComparer implementation that you can use with Array.Sort.
public class StringLogicalComparer : IComparer {
    public int Compare(object x, object y) {
        string text1 = x as string;
        if (text1 != null) {
            string text2 = y as string;
            if (text2 != null) return StrCmpLogicalW(text1, text2);
        }
        return Comparer.Default.Compare(x, y);
    }
    •••
}
This IComparer implementation could obviously be extended to support FileInfo, DirectoryInfo, or any other file-related classes you might want to sort based on the needs of your particular application.

Q I'm using DirectoryInfo.GetFiles in a console application to enumerate all of the files on various disks in my computer. When I attempt to do so for my CD-ROM drive when no disc is in the drive, I get a dialog box as shown in Figure 2. But as this is a console application, users aren't expecting to receive dialog boxes, and sometimes there aren't any physical users at the machine. Can you tell me what various options are available to me?

Figure 2 Error Dialog from DirectoryInfo.GetFiles
Figure 2 Error Dialog from DirectoryInfo.GetFiles

A The System.IO namespace uses the file management functions FindFirstFile/FindNextFile from the Win32® API to enumerate all of the files in a specified directory. When a serious system error is encountered, such as a drive that isn't ready, the default system behavior is to handle the error and display an error dialog to the user. You'll notice that after you get this dialog and continue out of it, you'll receive the expected System.IO.IOException that states "The device is not ready."

Kernel32.dll provides the SetErrorMode method that allows for this behavior to be changed, allowing the caller to specify whether the system will handle these sorts of errors or whether it will expect the process to handle them instead. For the .NET Framework 2.0, Directory.InternalGetFileDirectoryNames (on which DirectoryInfo.GetFiles relies) has been modified to use this method through P/Invoke in order to change the error mode so that this dialog is not displayed. To achieve this same functionality with the .NET Framework 1.x, you can do the same thing and manually P/Invoke to SetErrorMode.

Little code is required to make working with this very straightforward, as is demonstrated in Figure 3. The ChangeErrorMode struct implements IDisposable; in its constructor it changes the error mode to the specified value, and in its IDisposable.Dispose method it changes the mode back to its original value. This pattern makes it easy to incorporate this functionality into your application via the C# using statement:

using(new ChangeErrorMode(ErrorModes.FailCriticalErrors))
{
    string [] files = Directory.GetFiles(@"D:\");
}

Figure 3 ChangeErrorMode (Close Figure 3)
[Flags]
public enum ErrorModes
{
    Default = 0x0,
    FailCriticalErrors = 0x1,
    NoGpFaultErrorBox = 0x2,
    NoAlignmentFaultExcept = 0x4,
    NoOpenFileErrorBox = 0x8000
}

public struct ChangeErrorMode : IDisposable
{
    private int _oldMode;

    public ChangeErrorMode(ErrorModes mode)
    {
        _oldMode = SetErrorMode((int)mode);
    }

    void IDisposable.Dispose() { SetErrorMode(_oldMode); }

    [DllImport("kernel32.dll")]
    private static extern int SetErrorMode(int newMode);
}

When used with SetErrorMode, SEM_FAILCRITICALERRORS, defined as 0x1 in WinBase.h, causes the system to not display the critical-error-handler message box when a serious error is encountered. Instead, the system sends the error to the calling process, which is exactly the functionality you desire.

Q I'd like to interact with the Encrypting File System (EFS) from my .NET application, specifically determining what files have been encrypted and then encrypting and decrypting as appropriate. Is any of this functionality exposed to me?

A In the .NET Framework 2.0 Beta 1, the ability to encrypt and decrypt files is exposed through the File and FileInfo classes. Unfortunately, in the .NET Framework 1.x and later there is no exposed support for EFS, with the exception that the FileAttributes value returned from File.GetFileAttributes will tell you whether a file is encrypted. But as with most of the Win32 API, the magic of P/Invoke allows you to access the rest of EFS's functionality with a minimal amount of work.

In order to encrypt a file, decrypt a file, or determine the status of a file's encryption, there are three functions I can access through platform invoke: EncryptFile, DecryptFile, and FileEncryptionStatus, all of which are exposed from advapi32.dll. My P/Invoke declarations for them are shown here:

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool EncryptFile(string lpFilename);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool DecryptFile(string lpFilename, int dwReserved);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool FileEncryptionStatus(
    string lpFilename, out int lpStatus);

If all I'm concerned about is accessing this functionality, I can simply call these methods through these P/Invoke declarations. However, I almost always prefer to pass around FileInfo objects rather than string paths when working with files, and as such desire methods that can accept FileInfo instances instead of strings. In addition, these methods don't play as nicely with Code Access Security (CAS) as I'd like. Methods exposed through DllImport require the caller to have the UnmanagedCode security permission. Ideally, however, calling these methods would require nothing more and nothing less than the appropriate FilePermission for reading and writing the file in question.

Taking all of this into account, I've created the class shown in Figure 4. The Encrypt and Decrypt static methods accept a FileInfo as an argument and delegate to the unmanaged EncryptFile and DecryptFile methods, passing in the FileInfo's FullName. If the unmanaged methods return false, meaning that an error occurred, an exception is thrown. I've also included an enumeration, EncryptionStatus, which represents the possible lpStatus output values from FileEncryptionStatus. Note that the information provided by FileEncryptionStatus is much more informative that what is accessible through File.GetFileAttributes. GetFileAttributes can only tell you whether the file is encrypted or not, whereas if the file is not encrypted, FileEncryptionStatus can also tell you whether it can be, and if it can't, FileEncryptionStatus can tell you why.

Figure 4 Using EFS Functionality in .NET (Close Figure 4)
public class Efs
{
    public static void Encrypt(FileInfo file)
    {
        Validate(file);
        if (!EncryptFile(file.FullName)) throw new Win32Exception();
    }

    public static void Decrypt(FileInfo file)
    {
        Validate(file);
        if (!DecryptFile(file.FullName, 0)) throw new Win32Exception();
    }

    private static void Validate(FileInfo file)
    {
        if (file == null) throw new ArgumentNullException("file");
        if (Environment.OSVersion.Platform != PlatformID.Win32NT)
            throw new PlatformNotSupportedException();
        new FileIOPermission(
            FileIOPermissionAccess.Write | FileIOPermissionAccess.Read,
            file.FullName).Demand();
    }

    public static EncryptionStatus GetEncryptionStatus(FileInfo file)
    {
        Validate(file);
        int status;
        if (!FileEncryptionStatus(file.FullName, out status))
            throw new Win32Exception();
        return (EncryptionStatus)status;
    }

    [SuppressUnmanagedCodeSecurity]
    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    private static extern bool EncryptFile(string lpFilename);

    [SuppressUnmanagedCodeSecurity]
    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    private static extern bool DecryptFile(
        string lpFilename, int dwReserved);

    [SuppressUnmanagedCodeSecurity]
    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    private static extern bool FileEncryptionStatus(
        string lpFilename, out int lpStatus);

    public enum EncryptionStatus
    {
        CanBeEncrypted = 0,
        IsEncrypted = 1,
        IsSystemFile = 2,
        IsRootDirectory = 3,
        IsSystemDirectory = 4,
        Unknown = 5,
        NotSupportedByFileSystem = 6,
        UserDisallowed = 7,
        IsReadOnly = 8,
        DirectoryDisallowed = 9
    }
}

Prior to calling the unmanaged methods, the call is validated. This validation ensures that the code is operating on a Windows NT®-based platform (since EFS is only supported on NTFS) and demands read and write file permissions to the specified file. As a result, I've also attributed the P/Invoke declarations with the SuppressUnmanagedCodeSecurityAttribute so that calls to these methods don't trigger a stack walk, allowing classes that use the Efs class to do so without the UnmanagedCode security permission.

Note that EncryptFile, DecryptFile, and FileEncryptionStatus have all been declared with the SetLastError field set to true, meaning that the runtime will call the SetLastError Win32 API function before returning from the method. As a result, I can use Marshal.GetLastWin32Error, which retrieves the last error code set by a call to the Win32 SetLastError API method. However, since I want to return this information to the caller in the form of an exception, I can take advantage of the Win32Exception's default constructor which looks something like

public Win32Exception() : this(Marshal.GetLastWin32Error()) {}
This constructor delegates the result of GetLastWin32Error to the constructor that accepts the native error number as a parameter, which means I don't have to call it explicitly.

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

Stephen Toub is the Technical Editor for MSDN Magazine.


C++ Q&amp;A: Sending Keystrokes to Any App, Calling .NET from an MFC App, and More

C++ Q&A
Sending Keystrokes to Any App, Calling .NET from an MFC App, and More
Paul DiLascia

Code download available at: CQA0501.exe (231KB)

Q I'm trying to write an application that fills in forms by sending keystrokes to another application. Should I send WM_KEYDOWN and WM_KEYUP messages, or is there a better way?

Many readers

A You can probably get your app to work by sending WM_KEYDOWN and WM_KEYUP messages, but SendInput is an API function designed specifically for this purpose. It synthesizes input including keystrokes and mouse events by taking an array of INPUT structures, one for each input event—keystroke or mouse action. The INPUT structure holds a union that's either MOUSEINPUT or KEYBDINPUT (or HARDWAREINPUT, to simulate the toaster oven). For the keyboard it looks like this:

struct KEYBDINPUT {
   WORD wVk;      // virt key code
   WORD wScan;    // hw scan code
   DWORD dwFlags; // flags—see doc
   DWORD time;    // time stamp, 0 = dflt
   ULONG_PTR dwExtraInfo; // app-defined
};

Figure 1 Typematic Initial Dialog
Figure 1 Typematic Initial Dialog

So sending keystrokes to another application is basically a matter of building an array of INPUT structures, one for each keystroke (up and down), and then calling SendInput. To show how it works in practice, I wrote a little program called Typematic which lets you type your name, address, phone number, or other info quickly into forms just by pressing a hot key. It's the perfect thing for Web shopaholics. When you first run Typematic it displays the dialog in Figure 1, then goes into hiding. Thereafter you can press <WinKey>+T to reactivate Typematic and get the dialog in Figure 2 to see a list of abbreviations. Type "n" for name or "a" for address, and Typematic sends the corresponding string into the current form or application. The abbreviations are defined in a static table you can modify to use your own identity:

struct ABBREV {
   TCHAR key;
   LPCTSTR text;
} MYABBREVS[] = {
      { _T('n'),_T("Elmer Fudd") },
      { _T('a'),_T("1 Bunny Way") },
      ...
      { 0,NULL}
   };

Figure 2 Typematic Reactivated
Figure 2 Typematic Reactivated

In the real world, you wouldn't hard-code this information, of course; you'd provide a user interface for customizing it, and save the info in the user's profile so each user on the machine could have different settings.

Typematic illustrates a few other tricks: how to register a hot key to activate your app (see my December 2000 column) and how to make a static text control accept keyboard input (you have to handle WM_GETDLGCODE and return DLGC_WANTCHARS.)

Typematic defines a specialized static text control CStaticAbbrev that both displays the abbreviations and reads the accelerator key. Figure 3 shows the source. When the user presses the hotkey, Typematic wakes up and sets focus to this CStaticAbbrev control, which waits for a character. When CStaticAbbrev::OnChar gets a key that matches one of the abbreviations in the table, it hides the dialog, then calls a helper function SendString to send the text:

// in CStaticAbbrev::OnChar
if (/* find char in ABBREV table */) {
   GetParent()->ShowWindow(SW_HIDE); // hide dialog
   SendString(abbrev.text);          // send text
}

Figure 3 typematic.cpp (Close Figure 3)
#include "stdafx.h"
...
const HOTKEY = 'T';  // <WinKey>+T is hotkey to activate
static void SendString(LPCTSTR str);

class CStaticAbbrev : public CStatic {
protected:
   afx_msg UINT OnGetDlgCode() {
      return DLGC_WANTCHARS; // I want all chars
   }
   afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
   DECLARE_MESSAGE_MAP()
};

class CMyDialog : public CDialog {
public:
   CMyDialog(CWnd* pParent = NULL);
   ~CMyDialog();
protected:
   ...
   CStatic m_wndHelp;      // help message
   CButton m_wndOK;        // OK/Exit button
   CStaticAbbrev m_wndKeys;// static text to display/receive abbreviations

   UINT m_nIDHotKey;       // hot key identifier
   BOOL m_bFirstTime;      // first time activated?

   afx_msg LRESULT OnHotKey(WPARAM wp, LPARAM lp);
   DECLARE_MESSAGE_MAP()
};

struct ABBREV {
   TCHAR key;
   LPCTSTR text;
} MYABBREVS[] = {
   { _T('n'),_T("Elmer Fudd") },        // name
   { _T('a'),_T("1 Bunny Way") },       // addr
   { _T('c'),_T("Redmond") },           // city
   { _T('p'),_T("206 555 1212") },      // phone
   { 0,NULL},
};

BEGIN_MESSAGE_MAP(CStaticAbbrev, CStatic)
   ON_WM_GETDLGCODE()
   ON_WM_CHAR()
END_MESSAGE_MAP()

void CStaticAbbrev::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
   for (int i=0; MYABBREVS[i].key; i++) {
      if (nChar==MYABBREVS[i].key) {         // found:
         GetParent()->ShowWindow(SW_HIDE);   // ..hide dialog
         SendString(MYABBREVS[i].text);      // ..and send text
         return;
      }
   }
   MessageBeep(0);
}

LRESULT CMyDialog::OnHotKey(WPARAM wp, LPARAM lp) {
   if (wp==m_nIDHotKey && !IsWindowVisible()) {
      if (m_bFirstTime) {
         // first activation: replace initial message with abbrevs list
         CString text;
         for (int i=0; MYABBREVS[i].key; i++) {
            CString temp;
            temp.Format(_T("%c = %s\n"), MYABBREVS[i].key,
               MYABBREVS[i].text);
            text += temp;
         }
         m_wndKeys.SetWindowText(text);
         m_wndHelp.SetWindowText(
            _T("Type one of the keys below to enter text."));
         m_wndOK.SetWindowText(_T("E&xit"));

         m_bFirstTime = FALSE;
      }
      ShowWindow(SW_NORMAL);  // activate myself
      m_wndKeys.SetFocus();   // set focus to abbrev control
   }
   return 0;
}

static void SendString(LPCTSTR str) {
   INPUT inp[2];
   memset(inp,0,sizeof(INPUT));
   inp[0].type = INPUT_KEYBOARD;
   inp[0].ki.dwFlags = KEYEVENTF_UNICODE; // to avoid shift, and so on
   inp[1] = inp[0];
   inp[1].ki.dwFlags |= KEYEVENTF_KEYUP;

   for (LPCTSTR p=str; *p; p++) {
      inp[0].ki.wScan = inp[1].ki.wScan = *p;
      SendInput(2, inp, sizeof(INPUT));
   }
}

When Typematic hides itself, Windows® automatically restores the focus to whichever window previously had it, so the input goes to whichever window the user was working in before she pressed the hotkey. Pretty clever, eh? If you need to direct input to a specific application or window, make sure it's active before calling SendInput. SetForegroundWindow is the function to call.

All the work of sending the keystrokes happens in SendString, which builds the INPUT array and calls SendInput (see Figure 3). SendString sends a sequence of KEYDOWN/KEYUP pairs of INPUT structures, one pair for each character in the string. It uses the KEYEVENTF_UNICODE flag to send the string as Unicode characters. Unicode makes life easy because you don't have to synthesize capital letters using the Shift key. Without KEYEVENTF_ UNICODE, you'd have to send capital E as <Shift> followed by e, with down/up events for each, for a total of four keystrokes. Thank the Redmondtonians for adding KEYEVENTF_UNICODE!

If you're programming with Managed C++ or some other Microsoft® .NET-compliant language, sending keystrokes is even easier. There's a Framework class, SendKeys, with a static function Send that makes sending keys as easy as pie. You can even send special keys using squiggle syntax. For example "{F1}{BACKSPACE}A" sends F1, Backspace, A. Just for fun, I wrote another version of Typematic, Typematic.NET, that uses SendKeys. Now the SendString function is trivial:

#pragma managed
void SendString(LPCTSTR str) {
   SendKeys::SendWait(str);
}

What could be easier? When I first attempted this, I naturally tried SendKeys::Send, not SendWait. Why should I wait for the app to finish eating its keys? Alas, when I tried it, Typematic crashed miserably with System.InvalidOperationException somewhere in Windows.Forms.dll. When I invoked the common language runtime (CLR) Debugger to see what the heck happened, the Output window displayed the following message: "Additional information: SendKeys cannot run inside this application because the application is not handling Windows messages. Either change the application to handle messages, or use the SendKeys.SendWait method." Now that's what I call a friendly error message! Let this be an example to you all. But before you run off to use SendKeys, be advised that it's not as reliable as SendInput. You can discover this yourself by testing Typematic and Typematic.NET against different apps like Microsoft Internet Explorer, Notepad, an MFC form view, or some other favorite app. For some reason, SendKeys doesn't always work. I suspect it's a focus or timing problem—the keys are sent but then vanish into the ether because the window you think has the focus doesn't actually have it yet. So while SendKeys is easier to use and more powerful than SendInput, it doesn't work perfectly in all situations. Sigh. Maybe the Redmondtonians will fix it in the next release.

One final warning: sending keystrokes is a notoriously flaky way to control another app. You have to get all the keys exactly right, and the slightest change in context or user interface can throw everything off. If you're trying to control another app, look for a scripting system, programming interface, or macro language.

Q I've read several articles on how to disable system key sequences like Ctrl+Alt+Del, including one of yours in the September 2002 issue of MSDN®Magazine. But, how can I programmatically send Ctrl+Alt+Del?

William Burns

A The first question shows how to send keystrokes to any app, but you can't send Ctrl+Alt+Del using SendInput because this sequence is handled at a lower level in the operating system. Synthesizing keystrokes is not the proper way to "send" Ctrl+Alt+Del, anyway. What are you trying to do? If you want to invoke the task manager, call ShellExecute with "taskmgr.exe". If you're trying to reboot, you can call ExitWindowsEx with the EWX_REBOOT flag. ExitWindowsEx has all sorts of flags for shutting down or restarting the machine in different ways (see Figure 4). Also, your app needs the SE_SHUTDOWN_NAME privilege to reboot.

Figure 4 ExitWindowsEx Flags (Close Figure 4)

Shutdown Type | Description
EWX_LOGOFF | Shuts down all processes, then logs the user off
EWX_POWEROFF | Shuts down the system and turns off the power
EWX_REBOOT | Restarts the system
EWX_SHUTDOWN | Performs shutdown so it's safe to turn off the power
Modifiers | Description
EWX_FORCE | Force-terminates processes without sending WM_QUERYENDSESSION/WM_ENDSESSION
EWX_FORCEIFHUNG | Force-terminates processes if they don't respond to WM_QUERYENDSESSION/WM_ENDSESSION

Does anyone know the origin of Ctrl+Alt+Del? If you think you know, send me e-mail. I'll tell you the answer in a future column.

Q Can I call the .NET Framework from my MFC app? I want to call a managed class from my unmanaged MFC code, and I tried to do it by #using <mscorlib.dll>, but I got a message "/RTC1 incompatible with /clr." How can I call .NET from my MFC app?

Julian Kinsey

A Of course you can call .NET from your MFC app! Like everything else in Windows, it's easy once you know the correct voodoo. When you first create an MFC app, the App Wizard sets up all sorts of compiler options for you. One of the options under C/C++ Code Generation is "Basic Runtime Checks". When you create an MFC app, the App Wizard chooses "Both (/RTC1, equiv. to /RTCsu)" in your debug build to perform runtime checks like checking for bad stack frames, uninitialized variables, or buffer overruns and underruns. These checks are incompatible with /clr because managed code is totally different (it's Microsoft intermediate language, not native), but when you add /clr, the IDE doesn't automatically remove /RTC1. So you have to do it manually.

For each .cpp file in your project, set Basic Runtime Checks to Default. This is rather unfortunate, since for native/unmanaged functions, the checks are a good thing. They help you find bugs in your program before you ship. It's a shame to throw out the checks just because you want to call a .NET class or two. Isn't there some way to have your .NET and runtime checks, too?

The problem is that to use Managed Extensions to call the Framework, you have to set Use Managed Extensions to Yes, which sets /clr, and the only place you can do it is in the project settings, which are global. Use Managed Extensions turns /clr on for all modules in your project. By default, all your functions are now managed. If you prefer to make native mode the default, you can add a line at the end of your stdafx.h:

#pragma unmanaged

Since every module includes stdafx.h, now all your modules compile in native mode the way they always did. When it's time to call the Framework, you can flip to managed mode like so:

#pragma managed
void DoSomethingWithDotNET(...) {
   // call framework classes here
   // safe from managed functions
}

Until now, I've always used this trick (of putting #pragma unmanaged at the end of stdafx.h). But it doesn't solve the runtime-check problem because /clr is still incompatible with /RTC even if most of your functions are native. In mixed-mode apps the compiler doesn't let you say "do the runtime checks in native functions where they make sense."

Figure 5 Project Settings
Figure 5 Project Settings

What you really want to do is compile with /clr only the modules that actually call the Framework. But how can you do that, when Use Managed Extensions appears in the project-wide settings? Easy: you can always add specific switches in the Command Line section of the module's build properties. Figure 5 and Figure 6 show how to set this up. In your global project settings, set Use Managed Extensions to No, then add /clr to each module that calls the Framework. Now all the other modules can still use /RTC1 and get all the runtime checking goodies. Of course, now you have to move <mscorlib.dll> and all the other Framework files from stdafx.h to the managed modules. If you like, you can put all the standard .NET includes in one file UsesDotNet.h that looks something like this:

#using <mscorlib.dll>
#using <System.dll>
#include <vcclr.h>
using namespace System;

Figure 6 Module Settings
Figure 6 Module Settings

Then just #include "UsesDotNet.h" in each module that calls the Framework. You can still use #pragma managed/unmanaged to mix managed and native code within a module.

There's one more catch. When you add /clr to one of your modules, you also have to select "Not Using Precompiled Headers" in addition to turning off the runtime checks. Precompiled header information only works when all the modules have the same compile options; if one module has /clr, it can't use the same precompiled header as other modules that don't. Hey, computers are so fast these days, who needs precompiled headers anyway? If you want to get really fancy, you can always compile your managed modules through a different header file like UsesDotNet.h.

For a real, live, working project that uses the approach I've described, take a look at the Typematic.NET application from the first question in this column. You can download it from the MSDN Magazine Web site. All of the modules in Typematic.NET are compiled the normal native way, using precompiled headers through stdafx.h and no Managed Extensions. The only function that calls the .NET Framework, SendString, lives by itself in its quiet lonesome module SendString.cpp, which gets the managed compilation treatment with /clr, no runtime checks, and no precompiled headers. Enjoy!

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at www.dilascia.com.


{ End Bracket }: Joining the Team

{ End Bracket }
Joining the Team
Matt Pietrek

It's been more than a year since my words last floated across the pages of MSDN®Magazine. As readers of my blog probably know, my life's been interesting the past 12 months.

A year ago I was involved in my familiar routine working at Compuware's NuMega lab in Nashua, NH. Although I had often thought of working for Microsoft, I didn't think it was feasible since my young son spends most of his time in New Hampshire with his mother and I wanted to be nearby. But then a variety of events began to pull me towards Microsoft.

A good friend had recently joined the Visual Studio® team and told me wonderful tales of a developer's nirvana. Then I had a chance encounter with Don Box at the PDC 2003, where he asked "Why aren't you working for Microsoft?" Finally, there were the phone calls with Chris Sells that convinced me it could be possible to both work for Microsoft and see my son regularly. After the interviewing dust settled, I found myself saying goodbye to many close friends and leaving the East Coast for the Pacific Northwest where I am now happily ensconced.

Within the vast universe of Visual Studio, I'm working in the group responsible for some of the cool new features in Visual Studio Team System—specifically, the profiler, static analysis, and test authoring and execution pieces. I've worked most closely with the profiler team, doing both development on Visual Studio 2005 and investigating features for the next release, code-named "Orcas." As the development world moves to ever larger and more complex systems, tools that help the typical developer truly understand what's happening in a software system become more and more critical. At Microsoft, I have fantastic resources at my disposal to help make these tools a reality.

One of the most exciting things is how much more transparent the Microsoft development process is to the outside world. Key people across the company communicate directly with outside developers through blogs and other media. The attitude genuinely seems to be focused on showing why various decisions are made and being able to react quickly if it looks like we're headed in the wrong direction. Because developers at Microsoft are on the front line, we get to benefit from outside expertise as well as build a better user community. It's amazing to see the extent to which we are influenced by the expressed wishes of other developers.

Questions I get often now are "What's it like being at Microsoft after all those years of spelunking into Windows from the outside? Are you like a kid in a candy store?" The answer is both yes and no. It's perplexing to me that many people still think I focus heavily on undocumented topics. The reality is that most of my work and writing in the past five or six years was just exploring documented items that either needed more "big picture" perspective or which didn't have user-friendly documentation.

One of the great things about working in the developer division at Microsoft (which encompasses both Visual Studio and .NET) is that as new features and extensions are added, I can use them right away. For instance, in the .NET Framework 2.0, the Profiling APIs have more interfaces and methods with much more useful functionality (see Jay Hilyard's article in this issue of MSDN Magazine). Because I know that Visual Studio 2005 requires this latest version of .NET to run, I can use these new interfaces without worrying about supporting prior versions. The same goes for using the new Managed C++ syntax which makes .NET-based C++ code just as simple and even more powerful than C#.

The occasional downside to using these bleeding-edge technologies is that sometimes you bleed. There is an amazing level of coordination needed to keep hundreds of developers coding despite simultaneous major changes across the entire code bases of Visual Studio and the .NET Framework. Still, you get the occasional bum build that passed all the tests, but which still leaves you hanging because the one thing you need it to do isn't working. You need to be crafty and devise workarounds to keep moving forward.

As for having access to the source code, yup, I'm guilty of searching for my name to see how my articles for Microsoft Systems Journal and MSDN Magazine have affected the development of Windows® and Visual Studio. Incidentally, I highly recommend setting up an index server for your source base. It's a fantastic way to find things in a huge, unfamiliar codebase. While I could easily spend months and months poking around this way, the sheer volume of things I'm still learning, as well as keeping my little piece of the puzzle in good shape, keeps me plenty occupied.

Matt Pietrek has co-written several books on Windows system-level programming as well as the Under the Hood column for MSDN Magazine. Previously he was a primary architect for the NuMega/Compuware BoundsChecker series of products. He now works on the Visual Studio team at Microsoft.