The ASP Column

ATL Server Versus ASP.NET

George Shepherd

Code download available at:ASPColumn0311.exe(534 KB)

Contents

What Exactly is ASP.NET?
How is ATL Server Different?
ATL Server Versus ASP .NET
ASPX Files Versus SRFs
The "Intrinsic" Objects
Managing UI Elements
Session State
Conclusion

AWeb server's job is to accept incoming HTTP requests and return something useful to the caller (whether the caller is human or, in the case of Web Services, a machine). Windows® includes a mature infrastructure for handling requests—IIS and related extensions. However, programming IIS from scratch can be rather tedious and often error prone. A better way to manage HTTP requests is to work with one of the frameworks that sit on top of IIS. This month I'll compare the two main technologies for creating Web applications for Windows: ASP.NET and ATL Server. Each framework has some specific advantages and disadvantages. In this installment, I'll focus on managing a Web-based UI with ASP.NET. Next time I'll focus on building Web Services and other features using the two frameworks.

What Exactly is ASP.NET?

ASP.NET is a library of classes designed to handle HTTP requests. In addition to a class library, ASP.NET includes several IIS components for managing requests. These components include an ISAPI DLL named ASPNET_ISAPI.DLL and a worker process named ASPNET_WP.EXE. ASP.NET also installs new mappings in IIS, redirecting file requests for ASPX, ASCX, ASHX, and ASMX to ASPNET_ISAPI.DLL. From there, ASPNET_ISAPI.DLL directs the request to the ASPNET_WP.EXE, where ASP.NET loads the classes required to service the request.

ASP.NET has a convenient object model centering around a managed class named HttpContext. If you've ever written a standard ISAPI DLL, you're aware of the information contained in the EXTENSION_CONTROL_BLOCK passed into an ISAPI DLL's HttpExtensionProc. In managing a request, the ISAPI DLL picks apart that structure to get such information as the context ID, the query string, and functions to read and write to the client. ASP.NET wraps all this information in the HttpContext class. ASP.NET also includes the basic class infrastructure for managing Web-based UIs (through the System.Web.UI.Page class), and for managing Web Services (through the System.Web.Services.WebService class and the [WebMethod] attribute).

ASP.NET is object oriented. Every request passing through an ASP.NET application is handled by a class that implements an interface named IHttpHandler. This makes for a very extensible architecture. You may choose to take advantage of the ASP.NET page architecture or Web Services architecture, or you may write your handling logic from scratch. Figure 1 shows the path an ASP.NET request takes.

Figure 1 The Lifecycle of an ASP.NET Request

Figure 1** The Lifecycle of an ASP.NET Request **

The ASP.NET UI-handling architecture centers around the System.Web.UI.Page class (represented by ASPX files). ASPX files may include straight HTML code and server-side controls. When ASP.NET encounters a server-side control (via the "runat=server" property), it instantiates a class representing the control (for example, the Button and the ListBox controls). An ASP.NET page is basically processed as a tree of these server-side controls (literal text and tags on the page are packaged as a LiteralControl)—everything else is a server-side control. When the page is asked to render itself, it simply walks the control tree, telling each node in the tree to render itself. ATL Server works somewhat differently.

How is ATL Server Different?

ATL Server is a C++ template library for creating ISAPI DLLs. When IIS was first making its way in the world, developers had to write ISAPI extensions from scratch or from MFC's ISAPI DLL classes. Building ISAPI DLLs using raw C++ or MFC code required extensive hand coding. For example, the MFC ISAPI didn't include a forms-based architecture for developers. Any HTML tags that ended up on the client had to be emitted by hand.

ATL Server combines a forms-based approach with the runtime speed and flexibility of C++. A Web site built using ATL Server is made up of three basic components: the Server Response File (SRF), an application DLL, and the requisite ISAPI extension. The SRF is a new file type which ATL Server installs in IIS. The SRF mapping points IIS to your application's ISAPI DLL, which in turn directs processing to one or more of your application DLLs. SRFs include a special new tag syntax which basically calls entry points in your application DLLs. Figure 2 shows the path an ATL Server-based request takes through the system.

Figure 2 Path of an ATL Server-based Request

Figure 2** Path of an ATL Server-based Request **

ATL Server applications are comprised of a collection of DLLs (ISAPI extensions and application extensions) and HTML-generation templates called SRFs (mentioned earlier). This architecture clearly separates the app presentation from the application logic. A page's presentation is defined by an SRF containing HTML and special tags. These tags call into your ATL Server application DLLs.

As with most Web applications that target the Windows platform, ATL Server applications are built upon ISAPI DLLs. An ATL Server project includes a single ISAPI extension DLL that handles the raw request. ATL Server apps also include one or more application DLLs for more refined request processing. Classes for handling requests derive from CRequestHandlerT and contain your own specialized code for handling the tags in SRFs.

The handler classes contain dictionaries to associate request handler classes with request handler DLLs and associate replacement methods with SRF tags. In addition to the replacement dictionaries, CRequestHandlerT contains methods and member variables for accessing standard Web application elements such as form variables, cookies, request streams, and response streams.

When a browser surfs to a .srf URL via HTTP, IIS knows to open the site with the ATL Server application's ISAPI DLL. The ATL Server app then opens the SRF and loads the application's DLLs (if they weren't already loaded). The application then passes the request to the default request handler class, which parses the SRF looking for specially marked tags. Each time a tag appears in the SRF, the application calls a replacement method in a handler class residing in one of the application DLLs. The replacement methods generate the output to the browser dynamically. Figure 2 shows the path of a request through an ATL Server application.

ATL Server Versus ASP .NET

While ATL Server and ASP.NET are both based upon the ISAPI architecture, they handle requests very differently. To illustrate the differences, let's look at an example application that will collect the name of an individual and his or her development preferences. I'll show how to develop a user interface and how to use session state. In the next installment of this column I'll examine some of the other features (such as caching and browser capabilities) and how Web Services work on each framework. Figure 3 compares some of the features of the two frameworks.

Figure 3 Comparing Features of ASP.NET and ATL Server

Feature ASP.NET ATL Server
Recognized file types ASPX, ASCX, ASMX, ASHX, SOAP, ASAX SRF
Underlying architecture IIS IIS
View state Handled automatically Requires intervention
Session state Off, in-proc, state server, SQL Server Off, in-proc, OLE DB
Cache support Managed cache BLOB support
Web Service support [WebMethod] Implements class members

ASPX Files Versus SRFs

Both ASP.NET and ATL Server introduce new file extensions for the ISAPI architecture. ASP.NET introduces the file types ASPX, ASMX, ASCX, and ASHX, among others. Each of these file types has a corresponding managed class within the ASP.NET framework. In the case of ASPX files, that class is System.Web.UI.Page. The Page class is responsible for rendering a UI-based page. Figure 4 shows a simple ASPX file.

Figure 4 Simple ASPX File

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" Inherits="DevPreferencesASPNET.WebForm1" %> <HTML> <form id="Form1" method="post" runat="server"> <asp:label id="Label1" runat="server"> Type Name Here:</asp:label> <asp:textbox id="TextBoxName" runat="server"/> <asp:Label id="Label3" runat="server"> Type Age Here:</asp:Label> <asp:textbox id="TextboxAge" runat="server"/> <asp:dropdownlist id="DropDownList1" runat="server"> <asp:ListItem Value="I hate COM"> I hate COM</asp:ListItem> <asp:ListItem Value="I love C#"> I love C#</asp:ListItem> <asp:ListItem Value="I love ease of development"> I love ease of development</asp:ListItem> <asp:ListItem Value="I love .NET"> I love .NET</asp:ListItem> <asp:ListItem Value="Auto memory management"> Auto memory management</asp:ListItem> </asp:dropdownlist> <asp:label id="Label2" runat="server/> <asp:button id="ButtonSubmitMe" runat="server" Text="SubmitMe"/> <asp:label id="LabelFeature" runat="server"/> <asp:Label id="Label4" runat="server"> What's your favorite .NET Feature? </asp:Label> </form> </HTML>

The key things to notice about the ASPX file is the Inherits directive near the top of the page and the "runat=server" attributes with the buttons, labels, and dropdownlist tags. These tags represent server-side controls, for which Visual Studio® places corresponding classes in the codebehind file.

By contrast, SRFs contain mostly standard, plain-vanilla HTML tags. ATL Server does not have a server-side component architecture. However, it does introduce the notion of server response flags. These are special tags denoted by double curly braces ({{}}). When the ATL Server request architecture encounters a server response tag, it expects to find a corresponding handler function within the application DLLs. Figure 5 shows a simple SRF that displays roughly the same user interface as the ASPX example in Figure 4.

Figure 5 Simple SRF File

<html> <head> </head> <body> <form method="post" action="DevPreferencesATLServer.SRF"> <P> {{handler DevPreferencesATLServer.dll/Default}} This is a test: {{Hello}}</P> <P>Type name here: <INPUT id="Text1" type="text" name="TextBoxName"></P> <P>Type age here: <INPUT id="Text2" type="text" name="TextBoxAge"></P> <P> {{ShowNameAndAge}} <br> </P> <h2>What's your favorite ATL Server feature?</h2> <P><SELECT id="SelectFavoriteFeatures" name="SelectFavoriteFeatures"> {{ShowFeatureSelection}} </SELECT></P> {{ShowFavoriteFeature}} <P> <INPUT id="Submit1" type="submit" value="Submit Me" name="Submit1"> </P> </form> </body> </html>

The most important things to notice in this file are the special tags surrounded by double curly braces. For example, near the top of the SRF, you'll see the handler tag that specifies the default handler for the application ({{handler...}}). This tells the ATL Server application which DLL to load to find the functions to be called by the response tags. The other response tags refer to entry points in the application DLL.

The "Intrinsic" Objects

Both ASP.NET and ATL Server include similar intrinsic Request and Response objects, not unlike classic ASP. In ASP.NET, they are represented by the HttpRequest and HttpResponse classes. In ATL Server, they're represented by the CHttpRequest and the CHttpResponse classes. They serve generally the same purposes within each framework. The Request object encapsulates such items as the request parameters and request URL. The Response object includes functionality for outputting text to the client. For example, to insert "Hello World" into the output stream of an ASP.NET-based request, just call Response.Write, like so:

protected void HelloWorld() { Response.Write("Hello World"); }

To output "Hello World" to the client of an ATL Server-based application, use the CHttpResponse object, like so:

[tag_name(name="Hello") ] HTTP_CODE OnHello(void) { m_HttpResponse << "Hello World!"; return HTTP_SUCCESS; }

Notice how the OnHello function is called using the server response tag in Figure 5. (The tag looks like this: {{Hello}}.)

Managing UI Elements

Each of these frameworks takes a different approach to managing UI elements. As mentioned before, UI support in ASP.NET centers around the server-side control model. The presentation code (the ASPX file) declares server-side elements on the page using the "runat=server" attribute. As long as a corresponding class is declared in the codebehind class, accessing the controls programmatically is a breeze. For example, the code in Figure 4 shows several server-side control elements (the submit Button, the TextBox elements, and the DropDownList, for example). The codebehind page declares Button, TextBox, and DropDownList classes as members of the page, making the UI elements available programmatically. To find the data that's in the TextBoxName element, just access the Text property, like so:

String s = this.TextBoxName.Text;

ASP.NET server-side controls also have the advantage of tracking view state automatically. When the browser initiates a round-trip to the server, UI elements like dropdown listboxes and radio buttons retain their state consistently. For example, the last item selected in the dropdown listbox is the item that's shown. You don't need to write any special code to make the control behave correctly.

ATL Server has no such control model. The UI may be managed only through the server response tags. To populate the dropdown list, the ATL Server example includes a server response function to populate it (see the {{ShowFeatureSelection}} tag in Figure 5). Figure 6 shows the server response function that inserts the items in the dropdown list.

Figure 6 Managing a Dropdown Listbox within ATL Server

[tag_name(name="ShowFeatureSelection")] HTTP_CODE OnShowFeatureSelection() { const CHttpRequestParams& reqParms=m_HttpRequest.GetFormVars(); CString strFeature = reqParms.Lookup("SelectFavoriteFeatures"); this->m_HttpResponse << "<option "; if(strFeature == "I Love COM") { this->m_HttpResponse << " selected "; } this->m_HttpResponse << "> I Love COM </option>"; this->m_HttpResponse << "<option "; if(strFeature == "I Love C++") { this->m_HttpResponse << " selected "; } this->m_HttpResponse << "> I Love C++ </option>"; this->m_HttpResponse << "<option "; if(strFeature == "I Love speed") { this->m_HttpResponse << " selected "; } this->m_HttpResponse << "> I Love speed </option>"; this->m_HttpResponse << "<option "; if(strFeature == "I Love Control") { this->m_HttpResponse << " selected "; } this->m_HttpResponse << "> I Love Control </option>"; this->m_HttpResponse << "<option "; if(strFeature == "ATL Server works better with my COM components") { this->m_HttpResponse << " selected "; } this->m_HttpResponse << "> ATL Server works better with my COM components </option>"; return HTTP_SUCCESS; }

ATL Server does not track view state in the same way that ASP.NET does. Keeping the dropdown listbox consistent requires the kind of code shown in Figure 6. The code examines the parameters coming in on the query string to find out which item was selected. The rendering code ensures that the item selected by the user includes the "selected" attribute within the option tag.

Session State

Session state management in ASP.NET is extremely convenient. The ASP.NET worker process handles the grungy details. When a session is started by a new client, ASP.NET automatically allocates a new Session object—a dictionary containing name-value pairs. The System.Web.UI.Page class includes a reference to the current client's session information. Accessing the client's state simply involves accessing the session dictionary with an indexer. Figure 7 shows the code necessary for creating new session variables and accessing them later using ASP.NET.

Figure 7 Managing Session State in ASP.NET

private void ButtonSubmitMe_Click(object sender, System.EventArgs e) { Session["Name"] = this.TextBoxName.Text; Session["Age"] = this.TextboxAge.Text; SetLabel(); } protected void SetLabel() { string s = (string)Session["Name"]; if(s != null) { this.Label2.Text = "Hello " + s; } else { this.Label2.Text = "Who are you?"; } object oAge = Session["Age"]; if(oAge != null) { this.Label2.Text += " You're age " + oAge.ToString(); } else { this.Label2.Text += " How old are you?"; } }

Managing session state in ATL Server is a bit more complex. While the ISAPI DLL generated by the ATL Server Wizards includes a session manager, you still need to write the code for creating and accessing session objects. The code in Figure 8 shows how to create and access session variables in ATL Server.

Figure 8 Managing Session State in ATL Server

HTTP_CODE ValidateAndExchange() { // Get the ISessionStateService from the ISAPI extension HRESULT hr; hr=m_spServiceProvider->QueryService(__uuidof(ISessionStateService), &m_spSessionSvc) if(FAILED(hr)) return HTTP_FAIL; CCookie sessionCookie; sessionCookie = m_HttpRequest.GetSessionCookie(); if(sessionCookie.IsEmpty()){//no Session char newSessionID[MAX_SESSION_KEY_LEN]; DWORD temp=MAX_SESSION_KEY_LEN; m_spSessionSvc->CreateNewSession(newSessionID,&temp,&m_spSession); CSessionCookie cookieSession(newSessionID); m_HttpResponse.AppendCookie(cookieSession); } else { CStringA strSessionID; sessionCookie.GetValue(strSessionID); HRESULT hr; CComVariant vAge; CComVariant vName; hr = m_spSessionSvc->GetSession(strSessionID,&m_spSession); hr = m_spSession->GetVariable("Age", &vAge); if(SUCCEEDED(hr)) { m_nAge = vAge.lVal; } else { OnGetAge(); } hr = m_spSession->GetVariable("Name", &vName); if(SUCCEEDED(hr)) { m_strAge = vName.bstrVal; } else { OnGetName(); } } return HTTP_SUCCESS; }

The ValidateAndExchange function was generated by the ATL Server Wizard. This method is called at the beginning of each request in much the same way the ASP.NET Page class includes an Init and a Load event. ValidateAndExchange acquires an interface to the ISAPI DLL's session manager (via QueryService). If there isn't already a session cookie in the headers, the method creates a new session and adds a session cookie to the response. If this is a continuation of an existing session, the code uses the session information to populate the name and age variables. Notice that the session variables are represented by COM variants.

Conclusion

This month I took a quick survey of ASP.NET and ATL Server. These frameworks represent the current state-of-the-art in processing HTTP requests on the Windows platform. Each of these frameworks builds upon the time-tested ISAPI architecture that emerged nearly a decade ago. ASP.NET and ATL Server map their file extensions to specific DLLs. The ASP.NET DLL is ASPNET_ISAPI.DLL. The ATL Server DLL is generated by ATL Server, but for the most part it's boilerplate code. One advantage ATL Server has in this respect is that you may actually review (and change) the raw ISAPI request-handling code for your particular application.

While you may use either framework to write generally equivalent applications, the standard trade-offs apply here. It's usually very straightforward to develop an ASP.NET application using a managed language and the ASP.NET framework. However, you are at the mercy of the framework (though ASP.NET mitigates the flexibility issue by being very extensible). On the other hand, once your ATL Server application has gotten past IIS, it consists mostly of a bunch of DLL calls—a very high-performance proposition. ATL Server applications are written in C++, so you have a great deal of control, along with the requisite responsibility.

Next time I'll compare some other features of ASP.NET and ATL Server, including caching state, security, and Web Services.

Send your questions and comments for George 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 teaches seminars with DevelopMentor and is a contributing architect for Syncfusion's .NET tools.