HTML Applications

Power Up Your Web Applications with IE5

By Ken Ramirez

A great number of powerful features have been introduced with the release of Microsoft Internet Explorer 5. These enhancements include DHTML Behaviors which allow you to encapsulate common code into reusable modules, client capabilities which allow your Web applications to retrieve information about the client's browser, extended Cascading Style Sheet features, and more. The most appealing to me, however, is the ability to create HTML Applications.

This powerful new feature, known as HTML Applications (or HTA), brings new meaning to the term "thin client." To help understand its benefits, let's first look briefly at what it's designed to replace.

The Way It Used to Be

Traditional client/server applications normally split the work required of an application to achieve performance and scalability. In the past, the client was responsible for the user interface, input validation, and moving data in and out of a database.

When a database connection was required by the client, it normally opened the connection to the database as early as possible, and closed it as late as possible. In other words, the client created the connection as soon as it finished loading the application, but before the user was allowed to do anything. "As late as possible" means that the connection was closed as the application shut down.

The server, on the other hand, consisted of a database server, or a file server sharing a "desktop database" such as Access, xBase, or Paradox. The communication mechanism used by a typical client/server application to allow the client to access the data was (and still is) normally Data Access Objects (DAO), Remote Data Objects (RDO), or Active Data Objects (ADO).

With the popularity of the Internet and the expanded awareness of intranet applications, corporations all over the world are starting to move their applications to a multi-tiered platform, consisting of:

As mentioned, the user interface tier normally consists of a Web browser the end user utilizes to access the Internet or intranet application. As the user interacts with the application, Web pages are downloaded to the client browser and displayed to the user. When the user is prompted to enter information into the Web pages, the information is collected and then sent back to the server, where it is processed, validated, and possibly stored. Since the work performed by the Web pages and browser is minimal, the client side is said to be "thin." The thin client approach is better in terms of installation, maintenance, performance, and scalability.

These "traditional" thin clients consisted of various technologies. The majority of the work performed by the application took place on the server side of a multi-tiered system. Thin clients were also limited for security reasons. They were restricted from accessing the local machine's files (including the registry), and their every action was monitored by the browser.

HTAs, on the other hand, break all these restrictions. They're allowed free rein on the local machine, so end users must be aware they're about to run possibly dangerous code if they choose to run an HTA from somewhere on the Internet. Thus, when running an HTA from across the Internet, it's best to limit executables to well-known third-party vendors.

What Is an HTA?

It's important to note that you can continue to use your existing tools and skills to build HTAs. HTAs are Web pages built with Dynamic HTML (DHTML), scripting, and all the other components normally used to build your corporate applications. Essentially, you can take an existing Web-based application and turn it into an HTA.

However, your HTA will differ slightly in two regards. First, the application you build will provide its user interface in its own browser window, without the usual browser menus and toolbars imposed by the browser, i.e. it can define its own user interface for the menu items and toolbars. Secondly, HTAs aren't restricted as normal Web pages are. HTAs can do anything other locally-accessed applications can do.

So how do you create an HTA? You simply change the extension of your Web application's initial Web page to .hta. The .hta file extension tells the browser how it should treat the application, i.e. it determines if it should turn off security and allow the application to have access to the file system and registry. There is also a new tag, <HTA:APPLICATION>, that tells the browser window how it should behave. This tag, if it exists, should be placed somewhere between the <HEAD> and </HEAD> tag pair. This new tag has several attributes described in the table in Figure 1.

Attribute: Description

Notes

APPLICATIONNAME: Assigns a name to the HTA.

If you set the SINGLEINSTANCE attribute to "true," you need to provide an APPLICATIONNAME value, because it's used to determine if another instance of the application is already running. This attribute is available to scripting code as a read-only property. No default value.

BORDER: Determines the window's border type.

Possible values include: "thick" which instructs the window to create a thick border with a size grip and sizing border; "dialog window" which instructs the window to create a border that would resemble a dialog box window; and "none" which specifies a window with no border. This eliminates the system menu, caption, and minimize/maximize buttons. A value of "thin" creates a very thin border. The BORDER attribute only applies if the application has a title bar or caption. This attribute is available to your scripting code as a read-only property. Default value: "thick"

BORDERSTYLE: Defines the border style within the application's window.

Controls the area around the content pane in the window. There are five possible values: "normal" provides a normal border style with no added effect; "complex" provides a border that appears to be raised while also appearing sunken; "raised" provides a raised 3D border; "static" also provides a 3D border, however, this style is used when the window is not meant to accept user input; "sunken" provides a sunken 3D border appearance. This attribute is available to your scripting code as a read-only property. Default value: "normal"

CAPTION: Determines if a caption is displayed in the application window's title bar.

If this attribute is set to "yes" a caption is displayed in the application window's title bar. If it is set to "no" there will be no title bar, minimize/maximize buttons, or system menu. This attribute is available to your scripting code as a read-only property. Default value: "yes"

COMMANDLINE: Returns the parameter value passed to the application.

This read-only property is returned at run time to your script code and will only contain a value if the application was executed locally from the user's machine. If the application is executed on an HTTP connection, this property will contain no value. This property isn't available in an attribute format as are the other properties of the <HTA:APPLICATION> tag. No default value.

ICON: Specifies the icon displayed in the task bar when the application is running.

The icon must be in an .ICO file as a 32x32 image. The ICON property is read-only, and will provide the name specified in the matching attribute. Default value: system application icon

MAXIMIZEBUTTON: Determines if a maximize button is displayed on the title bar.

If the CAPTION attribute is set to "no," there will be no maximize button regardless of the value placed in the MAXIMIZEBUTTON attribute. This attribute is available to your scripting code as a read-only property. Default value: "yes"

MINIMIZEBUTTON: Determines if a minimize button is displayed on the title bar.

If the CAPTION attribute is set to "no," there will be no maximize button, regardless of the value placed in the MINIMIZEBUTTON attribute. This attribute is available to your scripting code as a read-only property. Default value: "yes"

SHOWINTASKBAR: Determines if the application's title and icon are displayed in the taskbar when the application is running.

Regardless of the value stored in this attribute, the application will still appear in the application list accessed via the [Alt][Tab] combination. This attribute is available to your scripting code as a read-only property. Default value: "yes"

SINGLEINSTANCE: Allows or prevents multiple instances of an application to be launched.

When set to "yes," the APPLICATIONNAME attribute is used to find other instances of the application. If another instance is located, the application is brought into view, but no second instance is launched. If the value is "no," multiple instances of the application are allowed to run simultaneously. This attribute is available to your scripting code as a read-only property. Default value: "no"

VERSION: Provides customizable version information to your scripting code.

You should commonly use a format such as MajorVersionNo.MinorVersionNo, e.g. 1.0. However, the parser makes no attempt to interpret the value of this attribute. It's only available to your scripting code as a read-only property. No default value.

WINDOWSTATE: Allows your application to set the state of the application's window.

A value of "minimize" causes the system to minimize the application upon launch. This value can also be used later to force the window into a minimized state. A value of "maximize" causes the system to maximize the application upon launch. This value can also be used later to force the window into a maximized state. The matching property can be accessed or set at run time from your scripting code. A value of "normal" gives the system the discretion of deciding how the window should appear. Default value: "normal"

Figure 1: The <HTA:APPLICATION> tag's attributes.

As an example, take a look at the code listing in Figure 2.


      <HEAD>

  <TITLE>Master-Mind Application</TITLE>
  <HTA:APPLICATION
    ID="oMMApp"
    APPLICATIONNAME="MMApp"
    BORDER="none"
    CAPTION="no"
    ICON="/images/MMLOGO.ico"
    SHOWINTASKBAR="yes"
    SINGLEINSTANCE="yes"
    SYSMENU="no"
    WINDOWSTATE="maximize">

  ...

Figure 2: Code example using the <HTA:APPLICATION> tag.

Here, the ID is set to "oMMApp." Setting an ID in this fashion allows you to access the <HTA:APPLICATION> tag from your script code more easily than if you didn't specify an ID. The APPLICATIONNAME is set to "MMApp." As mentioned in Figure 1, you must set this attribute if you plan on setting the SINGLEINSTANCE attribute to "yes." A path to the .ico file is specified in the ICON attribute. This allows you to create custom icons for your HTAs. The icon is then displayed in the caption, on the taskbar, and in the [Alt][Tab] pop-up window.

Also, note in this example that the SYSMENU attribute has been set to "no." This removes the application icon from the caption (which is normally the entry point to the system menu). It also removes the minimize and maximize buttons. Of course, this attribute never stands a chance of doing anything in this example, because the BORDER attribute has been set to "none." Doing so pretty much does everything that setting SYSMENU to "no" would do, and removes the caption (or title bar) and the window's border.

Security

HTAs are said to be fully trusted, so all bets are off when it comes to security. An HTA has the ability to execute any command code from within a script, and there are no restrictions imposed by the browser. In fact, HTAs are permitted to execute functionality that would otherwise never be allowed by a Web page. They have the ability to access the local machine's hard drive, registry, and have read and write permissions in both of these cases.

Normally, when a Web page downloads, the browser places the page in a zone according to the zone options set up by the user. If the options have stated that the user should be prompted before binary code is executed, the user is prompted. This includes ActiveX controls as well. However, HTAs are exempt from any zone rules. In fact, there is no zoning performed on an HTA. Once it's allowed to run, it can download any ActiveX controls it chooses to download and launch. The user will never be prompted before an ActiveX control chosen by the HTA is let loose to roam the hard drive.

Another security concern regards frames. Frames now carry an extra attribute that is passed back to the parent window of the HTA. This new attribute is named APPLICATION. If it contains a value of "yes," the frame is cleared of all restrictions. A value of "no" causes the frame to be placed in a zone governed by all zoning laws.

When the frame's APPLICATION attribute is given a value of "yes," it has the potential of opening a Web page within one of its frames. And although the page containing the frame might have been written by a trust-worthy source, who's to say that the pages within the frame wasn't written by some evil predator preying upon your unsuspecting hard drive?

For this reason, we can set the APPLICATION attribute to a value of "no." This places the frame in a protected zone, and prevents any of its pages from causing destruction on the local machine. The bottom line: If the frames are going to be displaying access pages publicly, don't give them APPLICATION status.

Deployment

Generally speaking, there are three ways in which an HTA application can be deployed:

The Web Model requires the development team to place the Web pages and components that make up the HTA on a Web site, just as you would for a regular Web site or application. When the users request the initial page (either via the Internet or an intranet), they'll be prompted to decide if the application should be opened or saved to their local machines. If it's saved, the HTA can be subsequently executed directly from the users' machines. Otherwise, each user will be running the pages directly from the Web site. If a user hasn't navigated onto a particular page, that page isn't downloaded. If a user has downloaded a page for viewing, further changes to that page are downloaded in a subsequent session.

The greatest advantage this method offers is that no code must ever be installed onto the users' machines. Because the users are almost always forced to go back to the server to retrieve and execute the application, they're always going to run the latest version of the application. The majority of the processing can continue to occur on the server machine. However, because HTAs can also take advantage of the client machine, they can offload some of the processing onto the client. Thus, the site will automatically increase its performance and throughput.

The HTA can also be packaged in an archive file, or exposed through a Web-enabled setup application. Once downloaded to the users' machines, the setup application can register the application and associate it with IE. Doing so causes the user to be warned before uninstalling IE, since dependent applications (such as HTAs) will no longer run. A great advantage of using a package approach is that the application can be executed as a stand-alone, just as if the users purchased the application over the counter and installed it on their machines. In fact, with HTAs, we may start to see EXEs go away in favor of a common thin client that is network and Internet ready.

Creating a hybrid of these two methods allows your application to maintain those components that may change often, or are seldom (if ever) used on the Web site. Thus, they're only downloaded as the user surfs over to them.

The Sample Application

The sample application accompanying this article (see end of article for download details) was created by initially developing a Microsoft Transaction Server (MTS) component with Visual J++, an ASP page to access the MTS component and return data to the client, and the HTA code, which is accessed from the client browser.

To provide something you could really sink your teeth into, I chose to use the Microsoft Northwind database. My MTS object exposes a method that allows its callers to retrieve the Customer data from the Northwind database. The ASP initiates an instance of the component, then proceeds to retrieve the data for viewing in the client browser. The data retrieval was performed using ADO. The entire system is then wrapped by the HTA application. It'll definitely pay to download the code, because there are so many real world components being integrated for the purpose of producing a real example.

The Visual J++ MTS Component

To begin, I generated a Visual J++ COM DLL using the File | New Project menu option. This generated a class similar to that shown in Figure 3.

/**
 * This class is designed to be packaged with a COM DLL output format.
 * The class has no standard entry points, other than the constructor.
 * Public methods will be exposed as methods on the default COM interface.
 * @com.register ( clsid=4A1EE401-FA91-11D2-9DA6-00107A1007A4, 
 * typelib=4A1EE402-FA91-11D2-9DA6-00107A1007A4 )
 * @com.transaction (required).
 */
public class Customer
{
}
Figure 3: Visual J++ class generated by the Project Wizard.

Now it was time to add support for ADO and return the Recordset to a caller, with all the Customer data. The ADO library was imported from the Windows Foundation Classes (WFC). One statement did the trick:


      import com.ms.wfc.data.*;

While I was at it, I decided that because I was going to use MTS anyway, I might as well import the appropriate classes. Thus, I added the com.ms.mtx package to the Java file:

import com.ms.mtx.*;

Finally, because I wanted to return the data in as static a state as possible, I added support for working with Variants. Using a Variant, I could call an ADO Recordset's getRows function to retrieve a static picture of the data collected by the Recordset. The getRows function always returns a multi-dimensional array inside a Variant. The array is, in fact, a Safe Array placed inside a Variant. This Variant is returned from the method we're about to meet.

To return the Recordset containing the Customer data, I outfitted the class with a method named GetData. I also placed the calls to retrieve the MTS context, create an exception handler block, and call the appropriate method in the context object (see Figure 4).

public Variant GetData()
{
  // Define DSN.
  String strCmd = "SELECT * FROM Customers";
  String strDsn = "DSN=Northwind; UID=sa; PWD=;";
  Variant vaResult = new Variant();
  
  boolean success = false;
    
  // MTS support code.
  IObjectContext context = null;

  // ADO support code.
  Recordset rs = new Recordset();

  try
  {
    // Get MTS context and set the return data.
    context = (IObjectContext) MTx.GetObjectContext();
      
    // Open the ADO connection, associate, and create
    // the recordset.
    rs.setCursorType(AdoEnums.CursorType.STATIC);
    rs.open(strCmd, strDsn);
    vaResult = rs.getRows();
    rs.close();
      
    success = true;
  }
  catch(Exception e)
  {
    success = false;
  }
  finally
  {
    // Let MTS know how we did.
    if (success)
      context.SetComplete();  // No error; complete transaction.
    else
      context.SetAbort();  // Error; abort transaction.
  }
  return vaResult;
}
Figure 4: The GetData function returns the data from the Customer table.

As you can see, the function creates two strings: one for the command string that will be executed against the database, and one for the data source name to open a connection to the database. A Variant is created to support the multi-dimensional array, and finally, the MTS context object is initialized and the Recordset object is created.

To support MTS, we need to retrieve our instance of the object context. We specified that an MTS transaction is required at the top of the file, with the statement:

* @com.transaction (required).

Therefore, an object context is created for us by MTS. The next few lines initialize the cursor type as static, execute the command string against the database, and store the result of the Recordset in the Variant. Now that the data has been moved into the Variant, the Recordset can be closed.

There's actually a performance trick here. One of the fundamental rules that must be followed to provide a successful MTS solution is that you must open a database connection as late as possible, and close it as early as possible. Another rule is that we must never hold on to a database connection between methods calls. Holding connections between methods calls was often done in the "old days" while developing client/server systems. Then it provided performance benefits; today it creates performance bottlenecks. Now you're encouraged to use connection pooling as much as possible. If you use IIS and related Microsoft technologies, you have nothing to worry about. Just open and close the connection as shown above, and IIS will do the rest.

I've included another performance trick as well. Any Web page (whether using Remote Data Services from the client side, or ADO from within an ASP page on the server side) using the Visual J++ MTS object I've provided, will never be slowed down by scripting code. Scripting code is what you would have to write to access the data from a Web page directly. By allowing the MTS component to access the data and return it (after the database connection has been released), the performance-critical code is placed in a compiled environment. You can get even faster performance by placing the MTS code under the control of C++, but Visual J++ or Visual Basic will fit the bill just fine.

Now that the component is ready, it's time to place it inside the MTS realm. I created a package named Customers and placed my Visual J++ Customer component into the package. Before writing the HTA application, I'll demonstrate a little cross-language support by providing a VB client solution to communicate with the Visual J++ MTS component.

Testing with a VB Client
The VB client is quite simple. I created a form with a few controls to display the number of columns and rows returned from the MTS component, a list box to display the names of each customer, and a button to allow the user to retrieve the data from the component. Figure 5 illustrates the VB form.

Figure 5: The sample VB form.

To create an instance of the MTS component from a VB application, we use the New keyword:


      Dim g_objCustomer As Customers.Customer

Private Sub Form_Load()
  Set g_objCustomer = New Customers.Customer
End Sub

Again, to make the server perform as little as possible, and to close the connection to the database, the customer records are returned to the client in a non-connected manner. Once the VB client receives the data, it proceeds to place it into the controls on the form, as illustrated in Figure 6.

Private Sub cmdGetData_Click()
  Dim rs As Variant
  Dim nCols As Long, nRows As Long, i As Long
  rs = g_objCustomer.GetData()

  nCols = UBound(rs, 1) + 1
  nRows = UBound(rs, 2) + 1

  txtCols = nCols
  txtRows = nRows

  For i = 0 To nRows - 1
    lstData.AddItem rs(1, i)  ' Add the name.
  Next i

  cmdGetData.Enabled = False
End Sub

Figure 6: The cmdGetData_Click procedure, called by Visual Basic when the cmdGetData button is clicked.

As you can see from this code example, after calling the MTS component's GetData method, the code enters a loop in which the data is moved into the list box on the form.

After this simple example, it's time to move on to a more interesting example - one involving Web pages, Visual InterDev, and ASP.

The ASP Example
I envisioned that the ASP example would include various features. For starters, I wanted to make the example look and feel as professional and realistic as possible. In this respect, Microsoft's built-in themes could probably help. After loading Visual InterDev and selecting File | New Project, I specified my theme, and was on my way.

I named the first file I created default.htm. This file acts as the default file, which is sent to the browser if someone reaches my site without specifying an HTML file. When loaded into the browser, the page resembles the screen shot in Figure 7.


Figure 7:
This default file is sent to the browser if someone reaches my site without specifying an HTML file.

The page describes the various technologies that went into building the site, along with a link to my company's home page (where I'll be posting this code, other related articles, and an errata page). But that's not the important part of this page. The main reason for this page is to provide a link to the CustomerList.asp. This link is in the form of an HTML form with one object: a button. When the form posts its result to the specified ASP (discussed below), the ASP page begins its work. This code sample illustrates the default.htm form:


      <FORM action="CustomerList.asp" id=FORM1 method=post name=FORM1>
  <INPUT type=submit value="View Customers" id=cmdSubmit name=cmdSubmit>
</FORM>

The ASP file named CustomerList.asp has the following responsibilities:

  1. Create an instance of the MTS component.
  2. Call the component's GetData method.
  3. Loop through the data returned from the component.
  4. Create and return an HTML table to be viewed by the user in the client browser.

We're required to write our ASP server-side code within <% and %> symbols. The code between the special symbols is the portion of the ASP that is processed on the server. Anything outside these symbols is returned to the browser as-is (plus or minus those parts controlled by the ASP code). The actual code I provided in the ASP is similar to the code in the VB example, except this time, we're returning an HTML table instead of filling a list box. Figure 8 depicts the result of executing the ASP.


Figure 8:
CustomerList.asp at run time.

Now, let's look at the body of the code (see Figure 9).

<BODY>
<H1><FONT color=silver>Master-Mind</FONT> Customer Site</H1>
<P>

<SCRIPT LANGUAGE=vbscript RUNAT=Server>
  Dim CustomerObj  ' Hold the MTS component.
  Dim rs           ' Hold the Recordset's Rows.
  Dim Rows         ' Number of rows.
  Dim Cols         ' Number of cols.
  Dim Col, Row     ' Indexes used in loops.
  Dim txt          ' Text to return to the browser.

  Set CustomerObj = Server.CreateObject("Customers.Customer")
  rs = CustomerObj.GetData()

  Cols = UBound(rs, 1) + 1
  Rows = UBound(rs, 2) + 1

  txt = "<TABLE border=1 cellPadding=1 cellSpacing=1 width='75%'>"
  For Row = 0 To Rows - 1
    txt = txt & "<TR>"
    For Col = 0 To Cols - 1  
      txt = txt & "<TD>"
      txt = txt & rs(Col, Row)
      txt = txt & "</TD>"
    Next
    txt = txt & "</TR>"
  Next

  Set CustomerObj = Nothing

  txt = txt & "</TABLE></P>"
  Response.Write txt
</SCRIPT>

</BODY>


Figure 9: This ASP code returns a table to the client browser.

Notice that after the MTS component is created, and the data is returned, the code enters a loop, which processes each record and each column with the record. The code first creates a table, proceeds to create table rows, then fills those rows with table details. All the HTML data is captured in a string named txt, which is written back to the browser using the Response object, as follows:

Response.Write txt

Now that the site almost looks like a real application, it's time to turn it into one. This can easily be achieved by turning the site's pages into a collection of HTA pages. I thought I'd have a little fun while I was at it, and chose to show the viewer some of the application's properties that are set during the page's startup with the <HTA:APPLICATION> tag.

The Final Form: An HTA Example
The only real requirement for creating an HTA application is that you must name the file with an .hta extension. Otherwise, the browser will assume the page is something else. As for the <HTA:APPLICATION> attribute, if you don't provide one, the default values described in the table in Figure 1 will be used. Again, to have a little fun, I displayed the chosen attributes on the page when the page is loaded, as depicted in Figure 10.



Figure 10:
This HTA example displays the chosen <HTA:APPLICATION> attributes.

The code to display this data is executed as soon as the page is loaded. Thus, I created a JScript method named window.onload, as shown in Figure 11.

cfunction window.onload()
{
  sString
 = "applicationName = " + oMMApp.applicationName
 + "\n" +
            "border          = " + oMMApp.border          + "\n" +
            "borderStyle     = " + oMMApp.borderStyle     + "\n" +
            "caption         = " + oMMApp.caption         + "\n" +
            "commandLine     = " + oMMApp.commandLine     + "\n" +
            "icon            = " + oMMApp.icon
            + "\n" +
            "maximizeButton
  = " + oMMApp.maximizeButton
  + "\n" +            "minimizeButton
  = " + oMMApp.minimizeButton  + "\n" +            "showInTaskBar
   = " + oMMApp.showInTaskBar
   + "\n" +            "singleInstance
  = " + oMMApp.singleInstance
  + "\n" +            "sysMenu
         = " + oMMApp.sysMenu
         + "\n"
 +            "version
         = " + oMMApp.version
         + "\n" +
            "windowState     = " + oMMApp.windowState     + "\n" ;
            
  oAppInfo.innerText = sString;
}

Figure 11: Displaying HTA attributes.

You'll also find a link that leads to a page that is similar to our default.htm page. From that page, we can get to CustomerList.asp page. Don't forget, within our HTA page, we have the power to access browser commands, the user's hard drive, the registry, and other system resources.

Conclusion
Although I've given you a starting point to begin producing your Internet/intranet solutions, I'm sure you'll find plenty of other uses for this interesting technology. In the future, I think we'll see various vendors begin to produce their products in the form of HTA applications - especially when this technology can be coupled with IE Behaviors to produce superior solutions.

There was a time when we thought that Java would be the replacement for applications written in other languages. These applications were supposed to be able to run on various platforms and different machines. However, we all know how the story ended. These types of solutions were too slow. This is the reason we have Visual J++ and WFC today. There is really no way to provide a realistic solution that can run on many different platforms without sacrificing performance. However, if IE becomes the platform used on the various platforms, including Macs, UNIX, and of course, Windows, the write-once-run-anywhere dream may truly become a reality, thanks to HTML Applications.

The examples referenced in this article are available for download here.

Ken Ramirez is co-founder of Master-Mind, a training and consulting powerhouse firm specializing in Internet and enterprise development. Ken is also co-author of WROX's Visual C++ MasterClass [1996] and the author of various articles. Master-Mind can be reached at http://www.mastermind.com, toll free at (877) MASTER-6, or via e-mail at kramirez@mastermind.com. Ken is available for consulting, mentoring, training, and speaking engagements.

Copyright © 1999 Informant Communications Group. All Rights Reserved. • Send feedback to the Webmaster