This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with Visual Basic
Download the code (39KB)


Ted Pattison

Creating Internet Applications with Visual Basic
     
In this and future installments of our new column, Advanced Basics, we'll show you how you can use Visual Basic to develop real-life Web solutions in conjunction with ASP, MTS, and other Web-centric Microsoft technologies.
Since this is the feature-length debut of what will be a regular column, I'd like to give you an overview of the topics I plan to cover in the next year. I will focus on writing Internet Information Server (IIS)-based applications using Visual Basic® as the primary development tool. I'll show you how to write server-side Visual Basic code that uses Windows NT middle-tier services such as Active Server Pages (ASP), Microsoft Transaction Server (MTS), and Microsoft Message Queue (MSMQ). If you're creating Internet-style applications using Windows NT Server, then this column should be right up your alley. I'll assume you are at an intermediate to advanced level with Visual Basic and are comfortable with the fundamentals of COM-style development. I'll also assume you have had some exposure to HTML and writing Active Server Pages using built-in ASP objects such as the Request, Response, Session, Application, and Server.
      I will focus on cross-platform development. The topics I write about should be equally useful to developers who are trying to reach users with a variety of browsers and platforms. This means you can create applications using IIS, MTS, MSMQ, and SQL Server™ that are accessible to users running Netscape Navigator on platforms such as Unix or the Macintosh.
      This month I will cover the core aspects of IIS programming with Visual Basic. I will examine the IIS runtime environment and discuss how the Windows NT 4.0 Option Pack release melded the MTS runtime environment with IIS. I'll also discuss creating and using Visual Basic objects within a Web-based application. Along the way, I will try my best to convince you to maintain the majority of your application logic in DLLs using Visual Basic as opposed to the combination of Active Server Pages with a scripting language.

Creating an Object from an Active Server Page
      Let's begin by examining the relationship between a Visual Basic object and the IIS runtime environment. Since you will be creating and configuring Visual Basic DLLs to run in the IIS environment, you should understand how COM activation and lifecycle management works when creating COM objects from an ASP script. Fortunately, IIS makes it relatively easy to create Web-based applications that use your server-side Visual Basic components.
      Active Server Pages are the key to running your code on the server. As you probably know, Active Server Pages are simple HTML text files that have an .asp extension instead of the .htm or .html extensions used by standard Web pages. When the IIS Web server determines that a client has requested a page with an .asp extension, it creates an ASP filter object to parse out all of the server-side processing instructions. This makes it remarkably easy to embed server-side logic in an HTML page using a scripting language such as VBScript or JavaScript.
       Figure 1 shows the code for a simple Web utility component created with Visual Basic named CTableBuilder. This component exposes the BuildTable method that accepts a row-returning SQL statement and returns an HTML table. The BuildTable method accomplishes its work by opening and enumerating through an ADO recordset to create an HTML table using standard tags. CTableBuilder allows you to maintain your ADO code inside a Visual Basic DLL, as opposed to maintaining the code in an ASP script. Here's an example of creating and using a CTableBuilder object from an ASP script:


 <%@ LANGUAGE="VBSCRIPT"%>
 <HTML>
 <HEAD><TITLE>My Flashy Page Title</TITLE></HEAD>
 <BODY>
 <H2>Customers Report</H2>
 <%
     Dim Builder, sProgID, sSQL
     sProgID = "TableBuilder.CTableBuilder"
     Set Builder = Server.CreateObject(sProgID)
     sSQL = "SELECT * FROM Customers"
     Response.Write Builder.BuildTable(sSQL)
 %>
 </BODY>
 </HTML>
      This snippet shows you how easy it is to create and use a Visual Basic object from an ASP page. Note that the ASP filter recognizes the inline tags <% and %> as the beginning and the end of a server-side script. Figure 2 shows what the resulting page looks like in a browser.
Figure 2: CTableBuilder Output
Figure 2: CTableBuilder Output

Once again, I must emphasize that this is a cross-platform solution; I just happened to use Microsoft Internet Explorer here. You can run as much COM code as you'd like on the Web server as long as you pass a pure HTML stream back to the client. It's up to you to know which HTML tags create browser-specific dependencies.

Replacing ASP Code with Visual Basic
      You enjoy quite a few benefits when you move your business logic and data access code out of ASP pages and into Visual Basic DLLs. ASP is ideal for small projects, but managing an application that contains thousands of lines of code is difficult. Here are some things to consider when deciding whether to use Visual Basic or server-side scripts in Active Server Pages:

  • Visual Basic offers much higher levels of encapsulation and code reuse because it is class-based and object-oriented.
  • You can access the Win32® API directly from an ActiveX® DLL. This isn't possible from a script in an Active Server Page.
  • Your Visual Basic code is compiled against type libraries, unlike ASP scripts, which use IDispatch and late binding. The Visual Basic IDE is superior to scripting because it offers things such as IntelliSense®, compile-time type checking, and direct vtable binding.
  • COM-based DLLs created with Visual Basic can take full advantage of the integration between the IIS and the MTS runtime environment.

      There are a few quirks involved when writing ASP code with VBScript. First, all your variables must be variants. This takes some getting used to for the average programmer. Second, VBScript can currently retrieve output parameters only when they are declared as variants. If you try to call a method on a Visual Basic object that has an output parameter defined as a String or a Double, the VBScript code will fail and generate a confusing error message. This implies that you should design your Visual Basic methods so that output parameters (such as ByRef) are variants when you intend to call them from ASP scripts.
      There's no problem propagating Visual Basic errors from a DLL back to an ASP client. Error handling isn't as graceful in VBScript as it is in Visual Basic. You must conduct error handling by using an On Error Resume Next statement and checking the error number after each call. This is somewhat cumbersome, but it works. Here's an example of handling an error raised in a Visual Basic DLL with a VBScript client:

 <% 
     On Error Resume Next
     Dim Builder, sProgID, sSQL
     SProgID = "TableBuilder.CTableBuilder"
     Set Builder = Server.CreateObject(sProgID)
     sSQL = "SELECT * FROM Customers"
     Response.Write Builder.BuildTable(sSQL)
     ' Test to see if call experienced a 
     ' runtime error
     If Err.Number <> 0 Then
         Response.Write "<b>There has been a 
             system error</b><br>"
         Response.Write Err.Description
     End If
 %>
IIS and MTS Marry
      The Windows NT 4.0 Option Pack provides a new level of integration between IIS and MTS. Figure 3 shows the IIS Web server process (inetinfo.exe). As you can see, this process loads the MTS Executive (mtxex.dll). Installing the Windows NT 4.0 Option Pack automatically creates an MTS library package named IIS In-Process Applications. If you locate this package in the MTS Explorer, you'll see that it is preconfigured with a set of Web application manager (WAM) components. WAM components are registered with MTS, which means that WAM objects are MTS objects. WAM objects communicate with the MTS runtime through their object context just like any other MTS object.
Figure 3: IIS Web Server Process
Figure 3: IIS Web Server Process

      A WAM object is responsible for creating an ASP filter when the IIS Web server processes an ASP page request. This ASP filter parses server-side scripts from the page and runs them before returning control to the client. Figure 3 shows a simplified picture of a single WAM object creating an ASP filter. In most cases, a client request for an ASP page passes between several WAM objects before being processed.
      WAM objects are very sophisticated in their use of thread pooling and thread dispatching. While the low-level details of how WAM objects work shouldn't concern you too much, you should understand that a WAM object is ultimately responsible for creating an ASP filter when a client requests a Web page with an .asp extension. I will revisit this topic and take a closer look at the COM object behind an ASP filter a little later.

Deployment Options
      If you want to run your Visual Basic objects inside the Web server process, you should properly register your DLL with the MTS Explorer. In many cases, registering your DLL with regsvr32.exe (the standard COM way) will produce the same effect as registering it with MTS. This isn't always the case. For instance, if you want your Visual Basic objects to run inside an MTS transaction, you must register your DLL with the MTS Explorer or the MTS services will not work properly. I recommend that you get used to registering your DLLs with MTS instead of regsvr32.exe. Once you register a DLL with MTS, you should always rebuild it with the Binary Compatibility option in Visual Basic.
      You can install the Visual Basic components from your DLL into the package named IIS In-Process Applications, or you can create another MTS library package and install your components in it. In either case, your objects will run inside the IIS Web server process.
      IIS 4.0 also lets you configure an ASP application (that is, a virtual directory) to run in its own dedicated process, as shown in Figure 4. Thus, you can isolate your ASP application from the IIS Web server process as well as from other ASP applications. If you want to configure your ASP application to run as a separate process, use the Internet Service Manager and select the Run In Separate Memory Space (Isolated Process) option in the Properties dialog box for the ASP application's virtual directory.

Figure 4: Isolated ASP Application
Figure 4: Isolated ASP Application

      Running an ASP application in isolation can provide greater isolation and fault tolerance in a production environment. You should note that this extra fault tolerance comes at a price. Every Web request must be redirected from a WAM object in the IIS Web process to a WAM in the isolated process, which has a negative impact on performance. Running your objects inside the IIS Web process will always be faster.
      When you're developing your components with Visual Basic, you'll probably want to run your ASP application as an isolated process. When you are writing and testing a Visual Basic DLL for an ASP application, you'll find that you need to unload the application's process before rebuilding the DLL. This is a real pain if you've loaded a DLL into the IIS Web server process because you have to shut down two separate IIS services before you can rebuild your DLL. However, the Properties dialog box for an isolated ASP application gives you the option of unloading the ASP application's process from memory. Once you unload the ASP application, you can rebuild any DLLs it has loaded.
      When you specify your ASP application to run in an isolated process, IIS configures your ASP application to run in a new instance of the MTS container application (mtx.exe). IIS also creates a new MTS server package dedicated to your ASP application. For example, if you mark a virtual directory named MindSite to run in isolation, a package named IIS-Default Web Site//Root/MindSite will be created automatically. You can install your Visual Basic components into this server package if you want your objects to run in this process. You can also create a secondary library package. Remember that objects created from an MTS library package will always be loaded into the process of their creator.
      Another deployment option is to run your Visual Basic objects inside their own MTS server package, as shown in Figure 5. To accomplish this, you should create a new server package with the MTS Explorer, and install your components inside it. What are some of the advantages of doing this? When you plan to execute MTS transactions from an ASP page, consider running your Visual Basic objects in a separate MTS server package for security reasons. Let's take a brief look at what you can do with a custom MTS server package.
Figure 5: MTS Server Package
Figure 5: MTS Server Package

      When you run your MTS transactions in the same process as your ASP code, you rely on IIS to authenticate each user. If the ASP application has been configured to allow anonymous access, any Internet user can run your transactions. If you run your transactions in a separate MTS server package, you can use the MTS role-based security model to give you more control over which users can run transactions. This makes it possible to grant access permission to authenticated users with Windows NT domain accounts, while denying access to anonymous logons on a component-by- component basis.
      In addition, when you run your components in a custom MTS server package, you can decide which Windows NT user account your Visual Basic objects will run as. When you create an MTS server package with the MTS Explorer, you can assign the package's identity to any local or domain account. This means that all your business logic and data access code will run with the credentials of a single, dedicated business account of your choosing. This is a common approach in a large three-tier system.
      What's really neat about these three different deployment options I've just described is that none of them change the way you write either your ASP or Visual Basic code. You can change from one of these deployment options to another simply by changing the attributes of ASP virtual directories and MTS packages; there's no need to change your code. For example, you can test your code in the development environment by running your ASP application as an isolated process. When it's time to put your code into production, you can run the ASP application in the IIS Web server process to achieve better performance.

Your New Runtime Environment
      The server-side objects you create with Visual Basic have complete access to everything in the MTS type library as well as the ASP type library. As a rule of thumb, you should include a reference to both of these type libraries whenever you create an ActiveX DLL project for the IIS environment.
      When you call Server.CreateObject from an ASP script, you pass a ProgID, and it returns a reference to a new object. As you've already seen, once you've created an object from an ASP page using Server.CreateObject, you can then invoke methods and access properties on the object as you normally would. Keep in mind that all calls from an ASP script into a Visual Basic object go through IDispatch.
      Creating an object by calling Server.CreateObject is similar to calling the CreateInstance method in the ObjectContext interface in MTS. A call to Server.CreateObject will always properly create an MTS object with its own context wrapper. This means that Visual Basic objects created in this manner can always call GetObjectContext to get a reference to the MTS object context.
      In addition to using the MTS type library, you can also program against the ASP type library. As I mentioned earlier, a WAM object is responsible for processing each request for an Active Server Page. When a WAM object processes an ASP request, it creates a COM object that serves as the ASP filter. Let's call this object an ASP scripting object. The scripting object scans through the ASP page and parses out any server-side processing instructions. IIS runs these server-side processing instructions, and then it returns an HTML stream back to the client.

Figure 6: Interobject Communication
Figure 6: Interobject Communication

      When you program against the five built-in ASP objects from a script in an Active Server Page, you are actually calling methods through this implicit scripting object. However, you can also program directly against these built-in ASP objects from inside your Visual Basic DLLs. Figure 6 shows how the lines of communication must be set up between the scripting object and a Visual Basic object. Once you create the connection from your Visual Basic object back to the scripting object, you can program against built-in ASP objects such as Request and Response from inside your Visual Basic DLL.
      An ASP scripting object exposes an interface named ScriptingContext that lets you get at the five built-in ASP objects. Before you can use any ASP objects in your Visual Basic project, you must reference the ASP type library. Figure 7 shows the ASP type library as seen through the Object Browser. Notice that the built-in ASP objects are exposed as COM objects.
Figure 7: ASP Type Library
Figure 7: ASP Type Library

      IIS 3.0 was the first release that let you program against the built-in ASP objects. The trick is to use the scripting object's ScriptingContext interface, which lets you navigate to the five other ASP objects. However, you'll need a little assistance to get to the ScriptingContext interface.
      When you call Server.CreateObject from an ASP page, IIS creates a new object and attempts to call into it through a special method named OnStartPage. If the call to OnStartPage succeeds, IIS passes a ScriptingContext reference to the new object. If the object doesn't implement OnStartPage, the call fails and IIS ignores the error. If you implement this method in a Visual Basic component, you can cache and use this ScriptingContext reference. The following code shows a Visual Basic class that does exactly this:

 Private ScriptCtx As ScriptingContext
 
 Sub OnStartPage(sc As ScriptingContext)
     ' Cache reference when called by IIS.
     Set ScriptCtx = sc
 End Sub
 
 Sub MyMethod()
     Dim rsp As Response
     Set rsp = ScriptCtx.Response
     rsp.Write "Hello World!"
 End Sub
      The previous code shows how to retrieve the Request object from the ScriptingContext interface. Once you retrieve a reference to a built-in ASP object, you can program against it as if you're in an Active Server Page. However, as you know, Visual Basic provides many advantages over writing ASP code, such as IntelliSense and compile-time type checking. Remember, this is all possible because you're programming directly against the ASP type library. Your code will also run much faster than VBScript code because it is compiled and it uses direct vtable binding instead of IDispatch.
      Using OnStartPage to get at the built-in ASP object is a technique that became obsolete with the release of IIS 4.0. Now you can get at the five built-in ASP objects without using the ScriptingContext interface. The built-in ASP objects are now available through ObjectContext interface in MTS.
      As you've seen, a call to Server.CreateObject creates a new MTS object with a context wrapper and an object context. Server.CreateObject also populates the object context's Item collection with the five built-in ASP objects. The following Visual Basic code shows how to retrieve the Request object using the ObjectContext interface:

 Sub MyMethod()
     Dim ObjCtx, rsp As Response
     Set ObjCtx = GetObjectContext()
     Set rsp = ObjCtx.Item("Response")
     rsp.Write "Hello World!"
 End Sub
This is a very powerful technique because it allows you to do so much more with Visual Basic. You can scrape values from the input controls in an HTML form, access the ServerVariables from the Request object, and create and access Session and Application variables. In short, you can do just about anything in your Visual Basic DLL that you can do in an ASP script.
      Here are some things to keep in mind. The scripting object created by IIS will only live for the duration of one page request. Once you retrieve a reference to one of the built-in ASP objects, you cannot hold it across multiple client requests. For that matter, the Visual Basic objects created from an Active Server Page should only live for the duration of the page itself. This means that your Visual Basic objects should also live and die within the scope of a single page request.
      The best way to write a Visual Basic class for IIS is to create a single method called Main. You should create an object in an ASP script and quickly pass control to the Visual Basic object by calling Main. You usually don't pass any parameters to Main because your Visual Basic code has access to the same information in the built-in ASP objects as the Active Server Page. Here's a minimal Active Server Page:

 <SCRIPT LANGUAGE=VBScript RUNAT=Server>
     Dim obj, sProgID
     SProgID = "MindSite.CMyWebComponent"
     Set obj = Server.CreateObject(sProgID)
     obj.Main
 </SCRIPT>
This Active Server Page doesn't even contain any HTML code for the user interface; it simply serves as the entry point into your Visual Basic DLL. You can create all the required HTML right inside your Visual Basic DLL. Figure 8 shows a Visual Basic class named CMyWebComponent that generates all the HTML for an entire page and streams it back to the client through the ASP Response object.
      As you can see, it's possible to do just about everything from Visual Basic. Whether you want to take it to this extreme is a matter of programming style. Let's look at some of the important issues that you must weigh when deciding how much logic you should put into your Visual Basic DLLs and how much you should leave in Active Server Pages.

Striking a Balance Between Visual Basic and ASP
      When you are designing any Web application that is less than trivial, you should adopt a three-tier mindset. This means that you should be partitioning your application logic into three discrete layers: presentation, business logic, and data access. Your decision is simple with the business logic and the data access code. Maintain your logic in a COM-based DLL. The decision is not as clear-cut regarding how to create the logic for the presentation tier.
      The problem with the presentation tier in a Web-based application is that you must construct the user interface using HTML. I've already shown that it's possible to maintain all the code for generating the HTML for an entire Web site in a Visual Basic DLL. Alternatively, you can maintain your HTML in Active Server Pages and simply call into your Visual Basic DLLs when you need to interact with business objects.
      Although I am an advocate of using Visual Basic whenever possible, I must admit it can be tedious to create and maintain a user interface with HTML using Visual Basic. This is especially true when an HTML editor like FrontPage® makes it so easy to whip up a good-looking user interface in ASP in seconds. You have to decide if generating lots of HTML in a Visual Basic DLL is worth the effort. I believe that for many companies the answer to this question will be yes.

Web Classes and the IIS Application Designer
      Visual Basic 6.0 has a new Web-based productivity enhancement called the IIS Application Designer (IISAD). This extension of the Visual Basic IDE provides a new type of component known as a Web class. The IISAD and Web classes address many of the problems associated with lots of HTML in a Visual Basic project. The way things work behind the scenes in an IISAD application is similar to the sample I provided earlier. The IISAD lets you create cross-platform Web applications by programming against the built-in ASP objects from inside the Visual Basic IDE. IISAD brings even more features to the table.
      One of the biggest benefits of the IISAD and Web classes is that they hide many of the tedious details of ASP programming from the developer using Visual Basic. For example, the IISAD automatically creates the ASP page that will serve as an entry point into your ASP application. This ASP page creates an instance of a Web class manager object that controls the entire IISAD application. The ASP page calls upon the Web class manager object to create instances of your Web classes.
      IISAD is a framework that is layered on top of ASP. What's more, the Web class type library adds a productive layer on top of the objects and interfaces in the ASP type library. The IISAD even gives you a way to debug your ASP code inside the Visual Basic IDE.
      Another nice feature of the IISAD is that it lets you work with HTML templates. Using these templates, you can keep the HTML layout details separate from your Visual Basic code. Once you import an HTML template into your project, you can perform a search-and-replace on certain tags and stream the results back to the client. This is a great way to create an entire Web site with a single project using the Visual Basic IDE.
      Web classes are an excellent method to deal with the presentation tier in an Internet application. The IISAD framework is somewhat complex, so before you start using it, you should have a solid foundation on how things work between IIS and Visual Basic. I promise to revisit Web classes in a future column and cover them in more detail.

Next Column
      In parting, I will leave you with one more piece of advice: never place a Visual Basic object into an ASP Session or Application variable. All your Visual Basic objects should be released when the Active Server Page that created them is finished. You'll have to tune in next time for a detailed description of why this is so. In my next column, I'll examine how to maintain state across page requests and show the best techniques for using Session and Application variables from a Visual Basic DLL. Along the way, I'll take a detailed look at the thread dispatching architecture of IIS and how your apartment-threaded Visual Basic components can peacefully coexist in such an environment.

From the December 1998 issue of Microsoft Interactive Developer.