-22-

Writing Server-Side Applications with VB 5 ActiveX Components

In this chapter, you will learn how to use Visual Basic 5 to write ActiveX components that you can use in server-side applications. You will look at creating business components and components to be used specifically in an Active Server Page environment. You will learn about the thread model supported by Visual Basic 5 components, and you will take an existing Visual Basic class and turn it into an ActiveX component. Creating OLEISAPI components is covered as well.

Overview of VB 5 ActiveX Components for the World Wide Web

Visual Basic 5 enables you to easily generate ActiveX components (called OLE servers in Visual Basic 4). The ActiveX components you create can be used by other applications or development environments, such as Microsoft Excel or Powersoft's PowerBuilder. You can use ActiveX components in a client/server environment, desktop environment, distributed computing environment, or Internet/intranet environment. This chapter focuses on creating ActiveX components to be used in an Internet/intranet environment--more specifically, the Internet/intranet server environment. You can use ActiveX components in a Web-based client much in the way you use ActiveX controls. Instead of an ActiveX control loading to the user's PC, the ActiveX component loads to a user's PC and is used via VBScript. This chapter focuses on building ActiveX server components. You will look at the following types of server components:

Active Server Pages provide the perfect environment for using ActiveX server components. Most of your Web-based server applications probably will be written using ASP, so this chapter concentrates on using ActiveX components in an ASP environment. The standard, non-ASP environment will be covered as well, via OLEISAPI.


NOTE: Throughout this chapter, when you see a reference to an ActiveX component, the reference is to an ActiveX component created with Visual Basic 5 unless otherwise noted.

Standard ActiveX Components

What this chapter refers to as standard ActiveX components are those components in your organization that can be used by any tool or application that supports ActiveX components. Standard components can be the business rules created by your company and used by other applications for validation and computations. The important point about a standard ActiveX component is that you do not have to modify it in any shape or fashion to use the component in any ActiveX-compliant tool or application, such as ASP, Visual Basic, or Microsoft Access.

ActiveX Components Designed for ASP

ActiveX components designed for ASP are components that take advantage of Active Server Page intrinsic objects or components. You build the component to use objects provided in the ASP environment. Because the components rely on ASP objects, they will not work outside of the ASP environment. These components could be business rules, utility functions, or other operations that are difficult to perform with standard Web-based tools.

OLEISAPI

When the Microsoft Internet Information Server (IIS) first shipped, ISAPI was (and still is) the API set provided to C and C++ programmers to extend the capabilities of the Web server. Visual Basic programmers were left out of the loop until Microsoft developed the OLEISAPI DLL (OLE2ISAPI.DLL) that enables Visual Basic programmers to use ActiveX components in standard HTML. This chapter will briefly cover OLEISAPI; however, I would no longer use OLEISAPI now that Microsoft Active Server Pages and the ActiveX Server framework are available. The ActiveX Server framework is a much better environment for using ActiveX components than OLEISAPI. I'll go into more detail about why I think OLEISAPI is outdated when the topic is covered in detail later in this chapter in the section "Using OLEISAPI."

Generating and Registering an ActiveX Server Component

ActiveX server components are simple to generate using Visual Basic 5. You can create components that are out-of-process (*.exe) or in-process (DLLs). Using out-of-process versus in- process is discussed later in this chapter in the section "Selecting the Proper Component Type (DLL or EXE) and Thread Model."


CAUTION: When creating server-side components, do not display any forms, dialog boxes, or message boxes. Server-side components do not have a user interface.

This section walks you through an example of creating an ActiveX DLL that you can use on a Microsoft IIS Web server. For this example, you will use the class DAOHTML, which was developed in Chapter 8, "Using Classes and Objects in Visual Basic 5."

Start a new Visual Basic ActiveX DLL project. Copy the file DaoHTML.cls from the CD-ROM at the back of this book to a working directory on your PC. Perform the following steps:

1. Add the class DAOHTML to the new project. Choose Project | Add Class Module to display the Add Class Module dialog box shown in Figure 22.1.

FIGURE 22.1. The Add Class Module dialog box.


2. Select the Existing tab. Then locate the file DaoHTML.cls copied from the book's CD-ROM. Select DaoHTML.cls and click Open. The class module DAOHTML is added to the project.

3.
Select the default class in the project Class1. Remove the class by choosing Project | Remove class1. The project now contains only the class module DAOHTML. In Visual Basic 4, an entry point was required when creating an ActiveX component. Typically, a module with a dummy procedure called main was added as the entry point. Visual Basic 5 no longer requires you to create a dummy entry point for ActiveX components. When generating DLLs, the startup object should be none, unless you want to initialize the DLL. To learn how to create an entry point for a DLL, proceed to the next step; otherwise, skip to step 6.

4.
Choose Project | Add Module to display the Add Module dialog box shown in Figure 22.2.

FIGURE 22.2. The Add Module dialog box.

5. Click Open and enter the following line of code in the Module window:
Sub Main
Press Enter. The line End Sub is added automatically. To add the entry point Main to your ActiveX component, you would select Sub Main in step 8 from the Project Properties dialog box. The entry point is called when your component is created using the CreateObject method or the keyword New. You then would add code required to initialize your component; dummy entry points are not required in Visual Basic 5. You should create entry points only when you want to initialize variables for the component. Remember that when a Visual Basic class is used to create an object, the class initialize event is invoked. The DAOHTML class uses DAO, so you must add a DAO reference to the project to create the object.

6.
To add the DAO reference, choose Project | References. The References dialog box appears. Enable the Microsoft DAO 3.5 Object Library checkbox and click OK (see Figure 22.3).

FIGURE 22.3. The References dialog box.

7. Choose Project | ProjectName Properties, where ProjectName is the name of your project, to display the Project Properties dialog box shown in Figure 22.4.

FIGURE 22.4. The Project Properties dialog box.

8. Use the Project Properties dialog box to name the project. The project name is important when creating an ActiveX component, because the project name will be part of the string used to reference and create the ActiveX component. Enter the name MyFirst in the Project Name textbox.
Notice that the Project Type drop-down combo box displays ActiveX DLL. The Project Type property tells Visual Basic what type of component to create. The Startup Object combo box defaults to None. For most ActiveX DLLs, you should use the default. If your component requires startup code, select Sub Main in the combo box (and complete steps 4 and 5). Then click OK.

9. Click the class module DAOHTML. In the Visual Basic Properties dialog box for the class, set the Instancing property to 5-MultiUse (see Figure 22.5).

FIGURE 22.5. The Properties dialog box.

10. Save the project by choosing File | Save.

11. Now create the component by making the ActiveX DLL. Choose File | Make MyFirst.DLL to display the Make Project dialog box shown in Figure 22.6.

FIGURE 22.6.The Make Project dialog box.

12. Click OK to create the ActiveX component.

You now have created an in-process ActiveX component. You can use the ActiveX component you created just like you would any other ActiveX component. First, you add a component reference to your project using the References dialog box. Then, you create a new instance of the component using Visual Basic 5, as this code shows:

Dim oTest As New MyFirst.DAOHTMLTable

The following example creates a new instance of MyFirst in an Active Server Page:

<% set oTest = Server.CreateObject("MyFirst.DAOHTMLTable")%>

Notice the string name used to reference an ActiveX component in ASP. The first part of the string is the name of the Visual Basic project used to create the component. The second part of the string is the module class name exposing the properties and methods you wish to use.

Registering the ActiveX Component on the Server

Before you can use your ActiveX component, you must register the component on your Windows NT Web server. Registering a component writes information about your component to the server's system Registry. You can automatically register an out-of-process ActiveX component (*.exe) by running the component on the server. To register an in-process ActiveX component requires the utility regsrv32.exe. This utility is located on the Visual Basic Professional and Enterprise CD-ROMs. If you installed Active Server Pages on your Windows NT Server and used the standard defaults, you can find the regsrv32 utility in the directory <drive letter>:\winnt\system32\inetsrv\asp\cmpnts.

To register your ActiveX in-process component, perform these steps:

1. On the server or machine on which you want to register the component, open a DOS command prompt window.

2.
Go to the directory where the regsrv32 utility exists.

3.
At the command prompt, enter the following code and then press Enter:
regsrv32 full path of the ActiveX component/component file name

A message box appears, telling you that your component has been registered successfully. To register the ActiveX component created in the preceding example (assuming that the component is stored in a directory called c:\components), you would enter the following at the command prompt:

regsrv32 c:\component\MyFirst.dll

Selecting the Proper Component Type (DLL or EXE) and Thread Model

Visual Basic 5 enables you to create ActiveX code components that are in-process (DLLs) or out-of-process (EXEs). Further complicating the type of component to create is the thread model to select. Visual Basic 5 enables you to mark components as thread safe and to generate components that support multiple threads or single threads. Because the focus of this chapter is on creating server-side ActiveX components that will be used in the Microsoft ActiveX Server framework, the type of component to generate is simplified, as well as the proper thread model. For ActiveX server-side components, should you create an in-process (DLL) or out-of-process (EXE) component? For the answer, read the following tip.


TIP: When creating Web-based server ActiveX components, create the component as an in-process component (an ActiveX DLL). Generating an ActiveX DLL provides better performance than out-of-process components, because the in-process component runs in the same process space as the application that uses the component (IIS). The application therefore can reference the component's properties and methods without making the costly cross-process calls required for out-of-process components. Out-of-process components do have some positive features not available to in-process components that can be used in a distributed computing or client/server environment, such as asynchronous callbacks or asynchronous notification events. In a Web-based environment, stick to using ActiveX DLLs.

The Visual Basic 5 ActiveX Component Thread Model

Before examining the thread model used by Visual Basic 5 when creating ActiveX components, take a quick look at the definition of a thread.

A thread is executing code. Every application in a Windows environment has at least a single thread of execution. An application or component is multithreaded if the application can create more than one thread of execution. Suppose that you have a financial database application and you have a computation that executes for a long time. If your application is multithreaded, you can start your computation by creating a thread to perform the computation. Then, while the computation thread is executing, you can begin to edit a database table using another thread. Multitasking preemptive operating systems, such as Windows NT and Windows 95, allocate separate time slices for each thread to execute (the computation thread and the edit table thread, for example), which gives the appearance of performing both tasks simultaneously. A thread model describes the environment in which a single or multiple thread executes and interacts with other threads. The Active Server framework supports several thread models. As a Visual Basic developer, however, you will be concerned only with the thread model used by all Visual Basic components (in-process and out-of-process), which is the Apartment Thread model shown in Figure 22.7.

In the Apartment Thread model, each thread contains its own container, or apartment, where all the objects created and used by the thread reside. The objects created in the apartment are unaware of other objects residing in other apartment threads. Each apartment has its own copy of global variables, which eliminates the possibility of multiple threads overwriting global data variables and makes the Apartment Thread model safe for use in multithreaded clients. Objects in the same apartment can share information via global variables without any performance penalty. Objects residing in other apartments can share information with each other by using object references. When objects communicate across apartments, a mechanism called cross-threaded marshaling is used. The performance of cross-threaded marshaling is slow and is comparable to the performance of cross-process marshaling used to communicate with out-of-process ActiveX components.

FIGURE 22.7. The Apartment Thread model.

The Instancing Property and Single or Multithreaded ActiveX Components

Every class you create with Visual Basic has an Instancing property, which determines how the class or object is created (see Figure 22.8). When developing ActiveX server components, you should create your ActiveX server components as DLLs (in-process components) most of the time to simplify the values of the Class Instancing property.

FIGURE 22.8 The Class Instancing property.

The default value of the Instancing property for an ActiveX DLL is MultiUse (an integer value of 5) and single threaded. For an ActiveX server-side component, always set the Instancing property to MultiUse. When a DLL is created using the MultiUse property and a single thread, a single copy of the object is loaded into the address space of the client. What happens when multiple users want to use the same object? To understand the behavior of a MultiUse component with multiple users, take a look at the example in Figure 22.9.

FIGURE 22.9. A MultiUse, single-threaded ActiveX component with multiple users.

The 401K object is used to manage an employee's 401(k) investment plan and has two methods: ComputeValue and ShowInvestments. The ComputeValue method takes several minutes to perform, and the ShowInvestments method occurs simultaneously. User A accesses a Web page that creates an instance of the 401K object, which is an ActiveX single-threaded DLL created with Visual Basic 5. User A begins to execute the ComputeValue method. User B accesses the same Web page and shares the same DLL for the 401K object being used by User A. User B executes the ShowInvestments method; however, the results are not returned immediately. Instead, User B finds herself blocked waiting for User A's ComputeValue method to complete execution. So what's going on? When the Instancing property of a component is set to MultiUse, a single instance of the DLL can be shared by multiple users simultaneously to create objects. What if users overwrite each other's data? No problem: To prevent user requests from overlapping and possibly overwriting global or local variables, the ActiveX component uses serialization. Serialization ensures that the component will execute only one user request at a time from start to completion. Pending requests are queued up and executed in turn. Using the example in Figure 22.9, User B is blocked waiting for User A's request to complete. Blocking quickly can become a big problem in an Internet/intranet environment. Fortunately, Visual Basic 5 enables you to create multithreaded ActiveX DLLs.


NOTE: You cannot spawn (create) a new thread by using a Visual Basic application or component. Multithreaded clients such as IIS, however, can take advantage of multithreaded ActiveX components and create multiple threads using a multithreaded Visual Basic component. When IIS creates an object from a multithreaded component, IIS creates a new thread for the object. If the Visual Basic component is marked as single threaded, IIS cannot create a new thread.

With Visual Basic 5, you can create multithreaded ActiveX DLLs. Use the example with the 401K object but, this time, assume that the DLL is created to be multithreaded. Examine Figure 22.10 to walk through the multiuser example and see how you can avoid blocking.

FIGURE 22.10. A MultiUse, multithreaded ActiveX component with multiple users.

User A accesses a Web page that creates an instance of the 401K object, which is an ActiveX multithreaded DLL created with Visual Basic 5. User A begins to execute the ComputeValue method. User B accesses the same Web page and shares the same DLL for the 401K object being used by User A. User B executes the ShowInvestments method, and the results are returned immediately. User B is not blocked waiting for User A's request to complete. Instead, in a multithreaded ActiveX component, when User B creates a 401K object, User B's 401K object creates a separate thread and apartment in which User B's objects can reside. Any object that User B creates will live inside User B's apartment thread, as shown in Figure 22.10. Objects in User B's apartment can communicate with each other, because they are in the same address space. Objects also can communicate with objects in other threads using cross-process marshaling. The cross-process call is serialized in the thread you are calling, and the calling thread is blocked until the request can be completed.


TIP: When creating ActiveX server components, you should create multithreaded DLLs. This provides you with the best ActiveX component performance and eliminates possible multiuser blocking during execution of lengthy methods. There is one drawback of a multithreaded ActiveX DLL created with Visual Basic: If a fatal error occurs in one apartment and terminates the thread, any other threads using the component also are terminated.

Creating Multithreaded ActiveX Server DLLs

When you create a multithreaded ActiveX server DLL, the component cannot require any user interaction. ActiveX DLLs that use any of the following cannot be multithreaded:

Message boxes and system-error messages are suppressed and can be written to the Windows NT Event log by setting the Visual Basic App object's LogMode property. To set the LogMode property, use the App object's StartLogging method. ActiveX DLLs that require user interaction or that contain controls, forms, ActiveX documents, or classes generated by ActiveX designers are only single threaded.

To create a multithreaded ActiveX DLL, perform these steps:

1. Make sure that the project does not contain any controls, forms, ActiveX documents, or classes created by using an ActiveX designer. If the project contains any of these elements, remove them. Otherwise, you will be unable to create a multithreaded DLL.

2.
From the Visual Basic main menu, choose Project | Properties. The Project Properties dialog box appears, as shown in Figure 22.11.

3.
In the General tab, enable the Unattended Execution checkbox.

4.
Click OK to save the change.

FIGURE 22.11. The Project Properties dialog box.

When the ActiveX DLL is compiled with the Unattended Execution checkbox enabled, a DLL that supports multithreading is generated. When creating multithreaded ActiveX components, keep in mind that Visual Basic DLLs use Apartment model threading and that ActiveX component automation uses serialization of requests to prevent multiple threads from executing a new operation before previous operations have completed. Keeping component serialization intact is important, because Visual Basic ActiveX components are not re-entrant. Re-entrancy is the capability of code to be executed by a thread, and, before the thread completes, the thread yields control of the processor to another thread to process the same code. When the second thread yields processor control, the variables and stack pointer are restored to the exact state prior to the processor yielding control to another thread. Visual Basic ActiveX components are not re-entrant, so when creating ActiveX components, do not do any of the following in your component, because it might cause the processor to yield to another thread before completing the current operation:

Now that you have learned that you should create ActiveX components that are multithreaded DLLs, start to think of all the different server-side ActiveX components you can use to energize your Web pages and how your Visual Basic skills can continue to aid you in the Web development environment.

Using OLEISAPI

When Microsoft released IIS, the ISAPI API was released to enable C programmers to write ISAPI programs and filters to extend the services provided by IIS. Well, what about the Visual Basic programmers? OLEISAPI was Microsoft's answer. OLEISAPI is a DLL provided by Microsoft that enables Visual Basic developers to use ActiveX components in Web pages with IIS. OLEISAPI was okay when it was the only way for a Visual Basic programmer to use ActiveX components with Microsoft's IIS. But with the release of IIS 3.0 and the ActiveX Server framework, I strongly urge you not to use OLEISAPI unless you are using an older version of Microsoft IIS that does not support Active Server Pages.

Here are a few reasons why using ActiveX components with Active Server Pages is preferred over OLEISAPI:

The bottom line is that, if you currently are using OLEISAPI and you can switch to Active Server Pages, do so! If you do not have Active Server Pages installed on your IIS, what are you waiting for? Time to upgrade!

If you still want to experiment with OLEISAPI, Microsoft includes OLEISAPI examples on the VB 5 CD as well as on its Website. You also must make sure that oleisapi.dll is in a directory with execute permission. When creating an OLEISAPI component, you must add the method you will use to invoke the component. If you want to submit a form for a guest registration database using the HTML form action Post, for example, you would add the following method to your class:

Sub DoGet(strHTMLrequest As String, strHTMLResponse As String)

   `Add code to parse the HTML string sent from the Server
   `Add code to determine the proper Method you wish to invoke for your component
       `For instance, if you were adding an entry to a Guest Registration Database           	                Âyou
       `could call a function or procedure called AddGuest
   `Add code to properly format an HTML response and then set the string
       `strHTMLResponse to the HTML string you want to send back to the browser.

End Sub

The following HTML shows how to call a component from a Web page using OLEISAPI:

<FORM ACTION="/oleisapi/oleisapi.dll/GuestAdd.WebOLE.DoPost" METHOD="Post">

In the HTML line using OLEISAPI, GuestAdd is the component, WebOLE is the Visual Basic class and DoPost is the method. One last comment on OLEISAPI: In the next two sections, you will see how much simpler it is to use existing and new ActiveX components in your Web pages, because they require fewer restrictions and programming than OLEISAPI.

Using Standard ActiveX Components

By using Active Server Pages, you can use existing ActiveX components in your Web pages that also can be used in any application or development tool that supports ActiveX components, such as Microsoft Excel, Powersoft's PowerBuilder, Borland's Delphi, or Microsoft's Visual Basic. Unlike OLEISAPI, ASP enables you to use existing ActiveX components in your Web pages without any component modifications required.

What kind of coding routines make good ActiveX components? The typical scenario given for generating standard ActiveX components is the three-tier architecture for software systems. The typical three-tier system consists of the upper tier, called user services, which provides user interaction services; a middle tier, called business services; and the bottom tier, which provides data services. In a three-tier architecture, ActiveX components are the perfect solution for the middle-tier business rules. ActiveX component reuse enables you to create the business rule one time and to use the rule-based component in other applications, including Web-based applications.

For this book, instead of creating a fictitious ActiveX business-rule component to use in an Active Server Page, you'll generate a component that enables you to use one of my favorite missing functions from VBScript in a Web page: the Visual Basic Format function. The Format function enables you to take a numeric or date expression and format the expression based on a specific format mask you provide. VBScript does not support the Format function, so to display a date in the format Tuesday, January 28, 1997 requires some work using VBScript, not to mention that changing the date format to 19970128 requires more VBScript work. The syntax for the Format function follows:

Format(express [, format[, firstdayofweek[, firstweekofyear]]])

where express is the numeric or date expression to evaluate, and format is the mask to use to properly format the expression. firstdayofweek and firstweekofyear are Visual Basic constants and optional parameters that will not be used in your ActiveX component. The ActiveX component will be a simple component that wraps a few lines of code around the Format function and exposes methods and properties that permit you to use the Format function in an Active Server Page. The ActiveX component is called FormatVB.DLL. The component will be generated as an in-process (DLL), multithreaded ActiveX component.


NOTE: The following code for the ActiveX component FormatVB.DLL is located on the CD-ROM that accompanies this book. The Visual Basic project name is FormatVB.vbp. The Active Server Page used to demonstrate the component also is on the CD-ROM and is called Format.asp.

The FormatVB component is based on the NumDate class, which has the following properties:

The Expression property is a variant data type and holds the number or date to format. The FormatMask property is a string that holds the mask to use to format the expression. The ErrorMessage property helps you debug the component. If an error occurs while formatting an expression, the ErrorMessage property contains the Visual Basic error message generated; otherwise, for successful operations, it is empty. The property FType determines whether the expression is a date or numeric value. FType is not really required for this component, because the Format function takes a variant data parameter for the expression. I included it, however, to show you how to use Visual Basic 5 enumerations. Enumerations enable you to define groups of constants. Even more important, when properly defined, enumerations show up in the Object Browser for your ActiveX component as well as in the Auto-Code feature of the Visual Basic Editor when setting your components properties that have enumerations.

The following enumeration, for example, declares two constants that will be assigned the values 0 and 1:

`Valid Property Values for the Type property
Public Enum FTypeValue
    ftDate
    ftNumeric
End Enum

Now, to tie the enumeration to a property to take advantage of the Auto-Code feature of the Visual Basic Editor and Object Browser, assign the enumeration type as the return or input data type of the property. For example, the FType Get property procedure follows:

Public Property Get FType() As FTypeValue
`used when retrieving value of a property, on the right side of an
`assignment.  Syntax: Debug.Print X.Type
    FType = mvarType
End Property

The FormatVB object contains a single method called DoFormat that returns a variant with the properly formatted date or numeric. The DoFormat method checks to see whether the Expression property contains a valid numeric or date. If the expression is a valid numeric or date, the DoFormat property executes the Visual Basic Format function and returns the formatted output. Listing 22.1 shows the entire code for the NumDate class.

Listing 22.1. The NumDate Class.

`local variable(s) to hold property value(s)
Private mvarExpression As Variant `local copy
Private mvarFormatMask As String `local copy
Private mvarErrorMessage As String `local copy
Private mvarType As Integer `local copy
`
`Valid Property Values for the Type property
Public Enum FTypeValue
    ftDate
    ftNumeric
End Enum
Public Property Let FType(ByVal vData As FTypeValue)
`used when assigning a value to the property, on the left side of an
`assignment.  Syntax: X.Type = 5
    mvarType = vData
End Property

Public Property Get FType() As FTypeValue
`used when retrieving value of a property, on the right side of an
`assignment.  Syntax: Debug.Print X.Type
    FType = mvarType
End Property

Public Property Let ErrorMessage(ByVal vData As String)
`used when assigning a value to the property, on the left side of an
`assignment.  Syntax: X.ErrorMessage = 5
    mvarErrorMessage = vData
End Property
Public Property Get ErrorMessage() As String
`used when retrieving value of a property, on the right side of an
`assignment.  Syntax: Debug.Print X.ErrorMessage
    ErrorMessage = mvarErrorMessage
End Property

Public Function DoFormat() As Variant
Dim vReturnValue As Variant

    `Set up an Error Handler
    On Error GoTo DoFormat_Error
    `Initialize the return values
    vReturnValue = ""
    mvarErrorMessage = ""

    `Check to make sure the expression to format is
    `valid for the type selected
    If mvarType = ftDate Then
        If Not IsDate(mvarExpression) Then
            mvarErrorMessage = "The value in the Expression " & _
                               "property is not a valid date."
        End If
    Else
        `Check for valid Numeric Value
        If Not IsNumeric(mvarExpression) Then
            mvarErrorMessage = "The value in the Expression " & _
                               "property is not a valid numeric."
        End If
    End If

    `If the expression is a valid date or numeric
    `perform the Format function.
    If mvarErrorMessage = "" Then
        vReturnValue = Format(mvarExpression, mvarFormatMask)
    End If

`Standard Exit
DoFormat_Exit:

    `Return the Formatted value
    DoFormat = vReturnValue
    Exit Function

`Standard Error Handler
`
DoFormat_Error:
    mvarErrorMessage = "Error Formatting expression. " & Err.Description
    vReturnValue = ""
    Resume DoFormat_Exit
End Function

Public Property Let FormatMask(ByVal vData As String)
`used when assigning a value to the property, on the left side of an
`assignment.  Syntax: X.FormatMask = 5
    mvarFormatMask = vData
End Property
Public Property Get FormatMask() As String
`used when retrieving value of a property, on the right side of an
`assignment.  Syntax: Debug.Print X.FormatMask
    FormatMask = mvarFormatMask
End Property
Public Property Let Expression(ByVal vData As Variant)
`used when assigning a value to the property, on the left side of an
`assignment.  Syntax: X.Expression = 5
    mvarExpression = vData
End Property
Public Property Set Expression(ByVal vData As Object)
`used when assigning an Object to the property, on the left side of
`a Set statement. Syntax: Set x.Expression = Form1
    Set mvarExpression = vData
End Property
Public Property Get Expression() As Variant
`used when retrieving value of a property, on the right side of an
`assignment.  Syntax: Debug.Print X.Expression
    If IsObject(mvarExpression) Then
        Set Expression = mvarExpression
    Else
        Expression = mvarExpression
    End If
End Property

The ActiveX component is complete; you're ready to test the component (remember that, with Visual Basic 5, you do not need a dummy empty procedure for an entry point when generating ActiveX components). The easiest way to test a standard ActiveX component is to use the same method used in Visual Basic 4 to test OLE Automation servers. From the Visual Basic design environment, run the ActiveX component project. Start a second Visual Basic session and add the ActiveX component currently running in the first Visual Basic session to the References dialog box of the new project. Create a test program that creates an object using your ActiveX component. Add code in the test program to set the methods and properties of your object. Then run and execute the test program. You can use the debugger in both Visual Basic sessions to debug your component or test program.

After the ActiveX component is tested properly, compile the ActiveX component to build the DLL. Don't forget to set the proper project options to make the DLL multithreaded. After you compile the ActiveX component, you need to register the DLL on your Web server using the regsrv32 program discussed earlier. You now are ready to use the ActiveX component on a Web page. Listing 22.2 shows the Active Server Page created to test the FormatVB component.

Listing 22.2. Source code to the Active Server Page to test the ActiveX component FormatVB.

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<!-- Active Server Page Example Using a Visual Basic ActiveX Component  -->
<!-- Author: Mark Spenik -->
<!-- Revision History: Jan. 31, 1997 -->
<!-- This example uses an ActiveX component to provide -->
<!-- the Visual Basic Format function in a Web Page.         -->
<!--                                                             -->
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
Function FormatExpression

    `Initialize the default string
    sExpress = ""

    `Create an instance of the ActiveX Component
    Set oFormat = Server.CreateObject("FormatVB.NumDate")

    `Check if it's a date - if so set the type property
    If IsDate(Request.Form("txtExpression")) then
        `Set the value to reflect a date
        oFormat.Ftype = 0
    Else
        `Set the required properties
        oFormat.FType = 1
    End If

    `Set the Expression property from the submitted form
    oFormat.Expression = Request.Form("txtExpression")

    `Set the FormatMask property from the submitted form
    oFormat.FormatMask = Request.Form("txtFormatMask")

    `Format the String
    sExpress = oFormat.DoFormat

    `Check For Errors
    If oFormat.ErrorMessage <> "" Then
        `Error found display back in the Web Page
        sExpress = oFormat.ErrorMessage
    End If

    `Cleanup - A good VB Practice - although with ASP not required.
    Set oFormat = Nothing

    `Return the string to the caller
    FormatExpression = sExpress
End Function
</SCRIPT>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Microsoft FrontPage 2.0">
<title>Home Page</title>
</head>

<body bgcolor="#000080" text="#FFFF00">

<p><font color="#FF0000"><marquee bgcolor="#FFFFFF">Testing a Standard ActiveX Component</marquee></font></p>

<hr color="#FF0000">
<!-- In this example, we are posting back to the same ASP file that -->
<!-- submits the request. As such first parse the form to see if it has -->
<!-- Been entered properly.  -->
<%
If Request.Form("txtExpression")="" Then
    sMsg = "Enter an expression to format."
ElseIf Request.Form("txtFormatMask")="" Then
    sMsg = "Enter a valid format mask."
Else
    sMsg = FormatExpression
End If
%>
Enter a date or numeric and a format mask. For example: <BR>
a date of Jan. 1, 1997 and a Format Mask of mmddyyyy will <BR>
return 01011997.
<form method="POST" name="frmExpression" Action="format.asp">
    <p>Expression <input type="text" size="22"
    name="txtExpression" VALUE="<%=Request.Form("txtExpression")%>"></p>
    <p>Format Mask <input type="text" size="22"
    name="txtFormatMask" VALUE="<%=Request.Form("txtFormatMask")%>"></p>
    <p><input type="submit" name="cmdFormat" value="Format"></p>
</form>
<hr color="#FF0000">
<!-- Send back the formatted Expression or instructions. -->
<B><%=sMsg%></B>
</body>
</html>

The Active Server Page shown in Listing 22.2 is a bit complex because the form posts the information back to the originating ASP file (Format.asp). To post information back to the originating ASP file, set the Action tag of the form to the originating ASP filename. In Listing 22.2, for example, the following line posts the form back to Format.asp, the originating file:

<form method="POST" name="frmExpression" Action="format.asp">

The following script examines the values of the Request object to determine whether the form has been submitted or is being loaded or refreshed:

<!-- In this example, we are posting back to the same ASP file that -->
<!-- submits the request. As such first parse the form to see if it has -->
<!-- been entered properly.  -->
<%
If Request.Form("txtExpression")="" Then
    sMsg = "Enter an expression to format."
ElseIf Request.Form("txtFormatMask")="" Then
    sMsg = "Enter a valid format mask."
Else
    sMsg = FormatExpression
End If
%>

If the form has been submitted, all the Request objects will have valid values, and the function FormatExpression is called (see Listing 22.2 for a full listing of the FormatExpression script). The following script found in the function FormatExpression creates an instance of the FormatVB object:

`Create an instance of the ActiveX Component
    Set oFormat = Server.CreateObject("FormatVB.NumDate")

The FormatExpression function returns a formatted string or error message. The output of the FormatExpression is returned to the browser with the following line of code:

<!-- Send back the formatted Expression or instructions. -->
<B><%=sMsg%></B>

Figure 22.12 shows the completed Web page.

FIGURE 22.12. An Active Server page to test the ActiveX component FormatVB.

Standard Component Recap

You should remember some of these important points about using standard ActiveX components on a Web page:

ActiveX Components Designed for ASP

In addition to generating standard ActiveX components that can be used in application or development environments, you can generate ActiveX components specifically designed to be used in the ASP environment. These components can interact with standard ASP objects, such as the Application or Response object. Building ActiveX components that interact with ASP objects requires registering the Active Server page DLL, asp.dll, on your local development machine and adding a project reference to the DLL. The DLL, as well as the Active Server Page environment, can be loaded by using Microsoft Visual InterDev or by copying asp.dll from your Microsoft IIS 3.0 (located in the directory \Intsrv\ASP) to your development machine. Although I slammed OLEISAPI because of the templates and modifications required to use a standard ActiveX component via OLEISAPI, I really like the capability to interact with the ASP environment directly via an ActiveX component written specifically for the ASP environment. I think you will find many instances when you will want to design components that you plan to use only in Web-based applications. Creating ASP ActiveX components enables you to write code and perform tasks in a familiar Visual Basic environment while interacting with ASP objects.

Generating Active Server Page ActiveX Components

The only difference between creating a standard ActiveX component and an ASP ActiveX component is that the ASP ActiveX component has a reference to the Microsoft Active Server Pages 1.0 Object Library, selected in the Visual Basic project References dialog box shown in Figure 22.13, and the component reference's ASP objects, such as the Response object in code.

FIGURE 22.13. The Visual Basic project References dialog box with the Microsoft Active Server Pages 1.0 Object Library selected.

Obtaining an ASP Object Reference in an ActiveX Component

The Active Server Page Object Library provides several object classes that can be used in Visual Basic ActiveX components. To use Active Server Page object classes in your Visual Basic ActiveX components, you must obtain a reference to the object you want to use. You can obtain an ASP object reference by passing an ASP object reference to your component (as a method parameter or by setting a property) or by using the ASP ScriptingContext class and the OnStartPage method (the OnEndPage method also is supported). Using the OnStartPage method is simpler, because a special object called the ScriptingContext object is passed automatically by IIS as a method parameter. The ScriptingContext object supplies methods that can be used to obtain references to the following Active Server Page objects:

By using the ScriptingContext object, your component can generate references to the desired ASP object without relying on the Web page author to correctly pass the required ASP references used by your component. Now look at a quick scenario on how the OnStartPage and OnEndPage methods are handled. When a Web page is retrieved that uses ActiveX components, the server looks for OnStartPage methods for all components on the page except those that have application scope. The OnStartPage method is executed for all the components before any script executing (remember that IIS passes a ScriptingContext object reference as a parameter in the OnStartPage method). When all the scripts on the page complete, the OnEndPage method is invoked for ActiveX components that have defined the method. The OnStartPage and OnEndPage methods are never called for application-scope ActiveX components. The only way to create application-scope objects from ActiveX components created with Visual Basic is to use the <OBJECT> tag in the Global.asa file. Any required ASP references must be passed into the component's methods or properties.

Using an ASP ActiveX Component in a Web Application

In this section, you'll enhance the timesheet application created in Chapter 21, "Active Server Pages, OLE DB, and Active Data Objects." You'll add an ActiveX component that displays previous timesheets added in an HTML table. You'll also modify the application to post the timesheet to the same ASP file instead of using multiple ASP files. The ASP ActiveX component will have a single method called GetEmpTime that will retrieve all the previous records entered by an individual and display them at the bottom of the HTML page. The Visual Basic ActiveX component will use the method OnStartPage to obtain a reference to the ASP Scripting Context object. The Visual Basic component will create an instance of the ASP Request object to read the employee name from the form to use for the timesheet retrieval query. An instance of the Response object will be created to send back the HTML table of timesheets entered to the client browser. ADO will be used to retrieve information from a Microsoft Access database. The ActiveX component also will log informational and error events to the Windows NT application Event log by using the Visual Basic App object.


NOTE: The Visual Basic 5 code for the component is located on the accompanying CD-ROM. The project file name is ASPComp.vbp. The Active Server page used to test the component is called SrvTimes.asp. The Microsoft Access database used is the Times.mdb database used in Chapter 21 for the timesheet application.

Figure 22.14 shows the completed Web page application with retrieved records.

FIGURE 22.14. The timesheet application using an ActiveX component.

The Visual Basic 5 source code for the component is a single class called GetTime. The class has no properties and a single method called GetEmpTime. The ActiveX component generated is called ASPComp.dll. Listing 22.3 shows the code for the GetTime class.

Listing 22.3. Source code for the GetTime class that makes up the ActiveX component ASPComp.

`Define a private Variable to use to generate any
`ASP object required for our component
Private goCurrentScript As ASPTypeLibrary.ScriptingContext

Public Function OnStartPage(oScriptContext As ASPTypeLibrary.ScriptingContext)
Dim strLogFile As String
    `This function is called when the Web page that uses
    `this component is being loaded. IIS will pass in the
    `references to the ASP ScriptingContext object.

    `Set a global reference to the Scripting Context object
    `to use in the Component's
`methods.
    Set goCurrentScript = oScriptContext

    `Let's start logging the component's progress
`using the Visual Basic App object.
    `On Windows NT the Log file is written to the Application
    `event log - on Windows 95 - to the File specified.
    strLogFile = App.Path & "\" & "axcomp.log"
    App.StartLogging strLogFile, vbLogAuto

    `Log the Startup Event
    App.LogEvent "OnStartPage Method Invoked for ASPComp",          
 ÂvbLogEventTypeInformation

End Function

Public Function OnEndPage()

    `Log the terminate event
    App.LogEvent "OnEndPage Method Invoked for ASPComp", vbLogEventTypeInformation

    `Clean up global object reference
    Set goCurrentScript = Nothing

End Function

Public Function GetEmpTime()
Dim oRequest As ASPTypeLibrary.Request   `Request Object
Dim oResponse As ASPTypeLibrary.Response `Response Object
Dim rsEmployee As New ADODB.Recordset
Dim strErrMsg As String, strSQL As String
Dim strHTMLReturn As String
Dim intNumOfColumns As Integer
Dim fldRec As Field
Dim colFields As Fields
Dim intCount As Integer `Generic Counter

    `Set up a generic Error Handler
    On Error GoTo GetEmpTime_Error
    strErrMsg = ""

    `Check for the global script context object
    If (goCurrentScript Is Nothing) Then
        strErrMsg = "Global reference to ScriptingContext object missing." & _
         " Make sure the object does not have Application scope and " _
         & " that you use Server.CreateObject."
         GoTo GetEmpTime_Exit
    End If

    `Obtain the required ASP references
    Set oRequest = goCurrentScript.Request
    Set oResponse = goCurrentScript.Response

    `Set up the SQL string required to retrieve all
    `timesheets for the current employee (i.e one submitted the form)
    strSQL = "Select Employee, WorkDate, ClientName, Billcode, Hours From EmpTime           ÂWhere Employee = `"
    strSQL = strSQL & oRequest.Form("txtEmployee") & "`"

    `Open a resultset based on the SQL Statement
    rsEmployee.Open strSQL, "DSN=TimeSheet"

    `App.LogEvent "All Objects Created.", vbLogEventTypeInformation

    `If No records in the recordset - exit now
    If (rsEmployee.BOF) And (rsEmployee.EOF) _
        Then GoTo GetEmpTime_Exit

    `Begin the Table format using the TAG Table
    strHTMLReturn = "<TABLE width=100% cellspacing=0 cellpadding=0 "

    `Add the table Background color
    strHTMLReturn = strHTMLReturn & "BGCOLOR = white ALIGN=Center>" & vbCrLf

    `Add the Table Caption
    strHTMLReturn = strHTMLReturn & "<CAPTION><B>" & _
                    "Previous timesheets for employee:  " & _
                    oRequest.Form("txtEmployee") & "</B></CAPTION><P>" & vbCrLf

    `Basic HTML string is set up - get the Number of Columns
intNumOfColumns = rsEmployee.Fields.Count - 1 `Make 0 based
    Set colFields = rsEmployee.Fields

    `Add column Headers
    For Each fldRec In colFields
        strHTMLReturn = strHTMLReturn & "<TH>" & fldRec.Name
    Next fldRec
    strHTMLReturn = strHTMLReturn & vbCrLf

    `Add The data rows
    ` Do until all the records have been processed
    While Not rsEmployee.BOF And Not rsEmployee.EOF
        strHTMLReturn = strHTMLReturn & "<TR>"
        For intCount = 0 To intNumOfColumns
            `Add proper HTML Tags for each column
            strHTMLReturn = strHTMLReturn & "<TD>" & _
                          rsEmployee(intCount) & "</TD>" & vbCrLf
        Next intCount
        strHTMLReturn = strHTMLReturn & "</TR>"
        `Get the Next Record
        rsEmployee.MoveNext

    Wend
    rsEmployee.Close
    `End the Table TAG
    strHTMLReturn = strHTMLReturn & "</TABLE>"
    App.LogEvent "Completed building table", vbLogEventTypeInformation

    `Send the table to the Browser
    oResponse.Write strHTMLReturn

`Single Exit Point
GetEmpTime_Exit:
    `Clean up any component references that were obtained
If Not (oRequest Is Nothing) Then
        Set oRequest = Nothing
    End If

    If Not (oResponse Is Nothing) Then
        Set oResponse = Nothing
    End If

    If Not (rsEmployee Is Nothing) Then
        Set rsEmployee = Nothing
    End If

    GetEmpTime = strErrMsg
    Exit Function

`Generic Error Handler
GetEmpTime_Error:
    `Get the Error Message
    strErrMsg = "Error retrieving employee time. " & Err.Description

    `Write to the application log
    App.LogEvent strErrMsg, vbLogEventTypeError
    Resume GetEmpTime_Exit
End Function

Now take a closer look at some of the code. The OnStartPage method is called when Active Server Pages is loaded. An instance of the ASP ScriptingContext object is passed into the method by IIS. A global private copy of the ScriptingContext object is set for later use, as shown here:

Set goCurrentScript = oScriptContext

The App object is used to set up message logging to the Windows NT Event log, as well as to log a message to the Event log using this code:

`Let's start logging the component's progress
`using the Visual Basic App object.
    `On Windows NT the Log file is written to the Application
    `event log - on Windows 95 - to the File specified.
    strLogFile = App.Path & "\" & "axcomp.log"
    App.StartLogging strLogFile, vbLogAuto

    `Log the Startup Event
    App.LogEvent "OnStartPage Method Invoked for ASPComp",          
 ÂvbLogEventTypeInformation

To read the submitted form values in the ActiveX component, instead of passing values through properties or methods, an instance of the ASP Request object is created to read the submitted HTML form. An instance of the ASP Response object is created to send information back to the browser. The following code uses the ScriptingContext object to create the ASP objects:

`Obtain the required ASP references
    Set oRequest = goCurrentScript.Request
    Set oResponse = goCurrentScript.Response

The code required to retrieve the HTML form value txtEmployee to set up the ADO query to retrieve previously entered timesheets from the database looks very similar to VBScript code used in ASP. The code follows:

`Set up the SQL string required to retrieve all
    `timesheets for the current employee (i.e one submitted the form)
    strSQL = "Select Employee, WorkDate, ClientName, Billcode, Hours From EmpTime
 Where Employee = `"
    strSQL = strSQL & oRequest.Form("txtEmployee") & "`"

The code to send the formatted HTML table back to the browser also looks very familiar (ASP):

`Send the table to the Browser
    oResponse.Write strHTMLReturn

Listing 22.4 shows the Active Server Page that uses the ASPComp.

Listing 22.4. Active Server Page script SrvTimes.ASP.

<HTML>
<!-- Active Server Page Example Using Active Data Objects -->
<!-- Author: Mark Spenik -->
<!-- Revision History: Jan. 6, 1997 -->
<!-- This example uses Active Data Objects to fill a combo box -->
<!-- and to store the data entered in the timesheet.           -->
<!-- An ActiveX component is used to display previous time entered. -->
<!-- This example posts the form back to this file and performs -->
<!-- all validation code on the Server. This ASP can be used in any -->
<!-- Browser.                                                       -->
<!--                                                                -->
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
Function AddTime
    `Create an instance of the ADO object
    Set oDBTime = Server.CreateObject("ADODB.Connection")

    `Open the database
    oDBTime.Open "TimeSheet"

    `Create an ADO recordset to add a new record
    set rsTimeSheet = CreateObject("ADODB.Recordset")

    `Set the Recordset Properties
    rsTimeSheet.CursorType = 0
    rsTimeSheet.LockType = adLockOptimistic
    rsTimeSheet.ActiveConnection = oDBTime
    rsTimeSheet.Source = "Select * From EmpTime"
    rsTimeSheet.Open

    `Check to make sure Updates are supported
    sResponse = "<H2>Sorry your timesheet was not added. Invalid server cursor.          Â</H2>"
    If rsTimeSheet.Supports(adUpdate) Then
        `Add a new record
        rsTimeSheet.AddNew
        `Set the data values
        rsTimeSheet("Employee") = Request.Form("txtEmployee")
        rsTimeSheet("WorkDate") = Request.Form("txtWorkDate")
        rsTimeSheet("ClientName") = Request.Form("cmbClient")
        rsTimeSheet("BillCode") = Request.Form("cmbCode")
        rsTimeSheet("Hours") = Request.Form("txtHours")
        `Add the record
        rsTimeSheet.Update

        `Check for Errors
        If oDBTime.Errors.Count > 0 Then
            Set oError = oDBTime.Errors(0)
            If oError.Number <> 0 Then
                sResponse = "<H3>Timesheet Error </H3><BR><P>"
                sResponse = sResponse & "Error adding your timesheet to the 			                  Âdatabase."
                sResponse = sResponse & "Error: " & oError.Description
            End If
        Else
           sResponse = "<H4>Timesheet Added for " & Request.Form("txtWorkDate") &                          Â". Scroll to the bottom of the page to view previous time added.</H4>"
        End If
    End If
    rsTimeSheet.Close
    oDBTime.Close
    AddTime = sResponse
End Function
</SCRIPT>
<HEAD>
<Title>Active Server Page TimeSheet Application</TITLE>
</HEAD>
<BODY BGCOLOR="Tan">
<CENTER>
<H2>Timesheet Application using Active Server Pages, ActiveX Components and  ÂADO</H2>
</CENTER>
<HR SIZE=2>
<!-- In this example, we are posting back to the same ASP file that -->
<!-- submits the request. As such first parse the form to see if it has -->
<!-- Been entered properly.  -->
<%
If Request.Form("txtEmployee")="" Then
    sMsg = "Enter an Employee's name in the timesheet."
    Session("TimeAdded") = False
ElseIf NOT (IsDate(Request.Form("txtWorkDate"))) Then
    sMsg = "You must enter a valid date in the date field."
    Session("TimeAdded") = False
ElseIf Request.Form("txtHours")= "" Then
    sMsg = "You must enter a valid number of hours."
Else
    Session("TimeAdded") = True
    sMsg = AddTime
End If
%>
<!-- Declare the start of the form. -->
<B><%=sMsg%></B>
<P>
<FORM NAME="TimeSheet" METHOD="POST" ACTION="SrvTimes.asp">
<B>Employee:</B> <INPUT TYPE="text" NAME="txtEmployee" Size=30 ÂValue="<%=Request("txtEmployee")%>">
<p>
<B>Select the Client</B>
<%  `Create an instance of the ADO Recordset object
    set rsClients = CreateObject("ADODB.Recordset")

    `Open a resultset based on the SQL Statement
    rsClients.Open "SELECT ClientName FROM Clients","DSN=TimeSheet"
%>
<!-- Notice in the following section the mixture of HTML with server side  Âscript -->
<SELECT NAME="cmbClient">
<% Do While Not rsClients.EOF %>
<OPTION><%=rsClients("ClientName")%>
<%
       rsClients.MoveNext
   Loop
   rsClients.Close
   Set rsClients = Nothing
%>
</SELECT>
<p>
<B>Select the billing code</B>
<SELECT ALIGN=CENTER Name="cmbCode">
<OPTION>Holiday
<OPTION>Vacation
<OPTION>Sick
<OPTION>Client Server Consulting
<OPTION>Work Group Consulting
<OPTION>Internet Consulting Services
</Select>
<p>
<B>Date:</B> <INPUT TYPE="text" NAME="txtWorkDate" Size=15 Value=<% =Date %>>
<p>
<%
If Session("Hours") = "" Then
   Session("Hours") = "8.0"
Else
   If Request("txtHours") <> "" Then
       Session("Hours") = Request("txtHours")
   End If
End If
%>
<B>Hours:</B> <INPUT TYPE="text" Name="txtHours" Size=4 Value="<%=Session("Hours")%>">
<HR>
<INPUT TYPE=SUBMIT VALUE="Submit Form">
<SCRIPT LANGUAGE="VBScript">
<!--
Sub window_onLoad()
Dim CurrentForm

   `Set the focus to the text box
   Set CurrentForm = Document.TimeSheet

   CurrentForm.txtEmployee.Focus
   Set CurrentForm = Nothing
end sub
-->
</SCRIPT>
<%
If Session("TimeAdded") Then
    `Create an Instance of the ActiveX component to send
    `back an Employee's time.
    Set oEmpTime = Server.CreateObject("ASPComp.GetTime")
    oEmpTime.GetEmpTime
End If
%>
</BODY>
<!--#include virtual="/ASPSamp/Samples/adovbs.inc"-->
</HTML>

Review the ASP script in Listing 22.4 carefully. SrvTimes.asp is very different from the timesheet application created in Chapter 21. Like the ASP used in the previous example, which uses standard ActiveX components, SrvTimes.asp posts the form back to itself, so all the code is contained in a single script file. Unlike the timesheet example created in Chapter 21, though, SrvTimes.asp performs all form validation on the server, so you don't have to be concerned about whether the browser supports VBScript. The hours submitted in the application default to 8 hours unless a previous number is entered (the timesheet application in Chapter 21 always defaults to 8 hours).

The script required to use your ActiveX component to produce the HTML table is quite simple:

<%
If Session("TimeAdded") Then
    `Create an Instance of the ActiveX component to send
    `back an Employee's time.
    Set oEmpTime = Server.CreateObject("ASPComp.GetTime")
    oEmpTime.GetEmpTime
End If

Components Odds and Ends

Before wrapping up this chapter, you should take a look at a few remaining topics to consider when creating Visual Basic 5 ActiveX components to be used in Web pages.

Application- and Session-Scope Threading with Visual Basic 5 ActiveX Components

As stated earlier, ActiveX components created with Visual Basic 5 use the Apartment Thread model and can be single or multithreaded. If you create an application- or session-scope object with a multithreaded ActiveX component that uses Apartment Model threading (created with Visual Basic 5), the object created will be only single threaded. For an application-scope object this could affect performance in a multiuser environment due to blocking.

Setting the Base Address of an In-Process Component (DLL)

The code base address of a DLL is the address in memory in which the code for your component is loaded into memory. Various processes can share the single memory copy of the code if there are no address conflicts. A conflict occurs when an in-process component's base address is being used by another in-process component or the executable. When a conflict occurs, the code must be dynamically relocated during the in-process load process, which slows down the component load process. The relocated code in memory for an in-process component generally cannot be shared by other processes.

The valid range of values for the base address is between 16MB (hexadecimal value 100000) and 2GB (hexadecimal value 80000000). The code in memory cannot exceed the 2GB range, so you need to obtain a code base that guarantees that the component's base address plus code size will not exceed 2GB. Base addresses are increased in multiples of 64KB (hexadecimal value 10000). The default address for ActiveX DLLs created with Visual Basic 5 is 285,212,672 (hexadecimal value 11000000). Never create components that use the default address assigned by Visual Basic, or all your ActiveX components created with Visual Basic will have conflicting base addresses.

To prevent components from having conflicting base addresses, you must develop a method to assign code base address that are not being used by other ActiveX components. You could create an application that generates and assigns random base addresses and store the assigned address and the component name in a database to be used for tracking and maintenance purposes.

To change the base address of a component, use the Compile tab of the Visual Basic 5 Project Properties dialog box shown in Figure 22.15.

FIGURE 22.15. The Compile tab of the Visual Basic Project Properties dialog box.

Component Compatibility and Testing

When testing a new ActiveX component, use the default Project Compatibility option on the Component tab of the Project Properties dialog box. Project compatibility enables your test projects to maintain the component reference during various test sessions by reusing the type library identifier (new type library information is generated). When you are happy with the component and create a DLL to install on various machines, switch to binary compatibility. Binary compatibility ensures that programs using a previous version of your component will be able to use the new version of the component without a problem; this compatibility ensures that previously defined methods and properties remain intact. A message box appears if the interface to the component has changed in a way that will affect programs using previous versions.

You should test ActiveX components before using them on a Website. Test your components using a test program that creates objects from your components and uses the exposed methods and properties. To test components that take advantage of ASP and ADO, try to test on a PC that has Visual Basic 5 and Active Server Pages running. You can use a Windows 95 machine running Microsoft's personal Web server with Active Server Pages or a Windows NT 4.0 machine with IIS 3.0. Also, when replacing an in-process component that has been used (loaded into memory), you will have to stop the Web server to overwrite the previous version.

Summary

You can create ActiveX components to take advantage of your current Visual Basic skills and easily enhance your Web application's functionality. Generate ActiveX multithreaded, in- process components (DLLs). Avoid using OLEISAPI; instead, use ASP. You also can create ActiveX components that use ASP intrinsic objects to retrieve information from a posted form or to send information back to a browser client.