Chapter 11

OLEISAPI


Object linking and embedding (OLE) has become Microsoft's interprocess communication (IPC) mechanism. It has effectively replaced dynamic data exchange (DDE) and to a lesser extent open database connectivity (ODBC) as the semigeneric application programming interface (API)-to-everything du jour.

OLE is Microsoft's model for software component construction-the blueprint for how software works together in the Microsoft environments. Unlike its predecessors, it shows no signs of slipping into obsolescence.

It follows that OLE is the favored mechanism for extending Microsoft's Internet Information Server (IIS).

This chapter introduces you to the basics of OLE Internet Server Application Programming Interface (ISAPI), the Microsoft-supplied means to customize IIS using OLE and Visual Basic (VB) 4.

In this chapter:

What Is OLEISAPI?

OLEISAPI supplies developers with the means to extend Microsoft's IIS. It is both a module and a mechanism that supplies hooks and entry points you can use to create custom Web server extensions using C++ or 32-bit VB4 (see Figure 11.1).

OLESAPI is Microsoft's alternative to the common gateway interface (CGI), the tool that has facilitated interactivity on the Web. It is the server-side "smarts" that allow specific client requests to be intelligently processed by the server on a custom basis.

OLEISAPI programs live in a twilight world between "systems" and "applications" code. They act as intermediaries between an HTTP client and the resources available to the IIS HTTP server, supplying customized processing and responses via predefined hooks.

The easiest way to take advantage of these hooks is with OLEISAPI.DLL. This is a dynamic-link library (DLL) thoughtfully supplied by Microsoft that allows you to write OLEISAPI components in VB4.

Without OLEISAPI.DLL, you would have to write your IIS Internet customizations in C/C++. You can also use Delphi to create ISAPI filters and extensions

Any developer who can create OLE server DLLs (in VB4 or C/C++) can build OLEISAPI applications. Figure 11.1 is a conceptual overview of OLEISAPI.

Fig. 11.1

A conceptual overview of OLEISAPI.

OLEISAPI.DLL has a limitation of 4 kbytes on the output string. Some versions available on the Internet have raised this limitation to 64 kbytes. This should be large enough to handle anything. For those comfortable with C++, the source code is available on the CD so you can change it yourself.

OLEISAPI 2.0: the Next Step

At this writing, a new version of OLEISAPI, OLEISAPI 2.0, is being released publicly for beta testing.

OLEISAPI 2.0 is considerably different in its implementation but the concept is similar. OLEISAPI 2.0 is actually an OLE server. Instead of calling a method in your OLE server that expects two string parameters, OLEISAPI passes a request object, and the return page is generated by that request object's methods.

In this chapter, I include notes on how you would do this differently in OLEISAPI 2.0. You are encouraged to change the source code yourself.

Development Environment and Tools

You need several items to construct OLEISAPI programs. Note that I have chosen the latest and most stable components as my tools.

You may be able to replace something listed for something not listed. But doing this is like installing NT on a system with a peripheral device that is not on the compatibility list: avoid if possible. You'll save a lot of frustration and lost time.

Development Operating System

We chose NT 4 as our development operating system for several reasons:

  1. It's durable (fully 32-bit with no Win 3.x "baggage").
  2. It runs IIS (in contrast to Win95, which does not).
  3. We can run IIS, Microsoft Internet Explorer (IE), and VB4 all on one computer.

If NT 3.51 is still available at the time of this publication, you can use it as well. You need NT 3.51 Service Pack 4 to use OLEISAPI extensions.

Since Microsoft evolves beta releases into commercial releases ever more quickly, by the time you read this you will probably only have access to NT 4.x anyway. All the better.

HTTP Programs

You will need an HTTP server (also called a Web server) and an HTTP client (browser). A browser object is available from Microsoft in the form of a custom control for VB4 but it is in its infancy. Better to choose a "full-grown" product than try to build a browser at this point.

As for the server, use IIS as it comes with NT4 server (or get a copy if you are using NT4 workstation). It's free, it's fully integrated with NT's security mechanisms, and it's simple to administer.

For your browser, you can use either Netscape or IE. Something to consider here is the customer you are writing your OLEISAPI application for.

If the customer already has Netscape or if you think most of the clients will be running Netscape when they run your OLEISAPI application, use Netscape as your development browser. Be aware that Netscape is not free and has somewhat intricate license requirements for commercial use.

IE Version 3 and later allow ActiveX Serve controls, which can add greatly to the presentation and use of your Web page. If you are developing OLEISAPI applications, you'll probably have the opportunity to use ActiveX as well (perhaps instead of Java). Also, IE is free.

Language Tool

Any language compiler/interpreter that can generate OLE server DLLs can be used to build OLEISAPI programs. I suppose there is a FORTRAN for NT or Lisp for '95 that you could use but stick with 32bit VB4.

In extreme cases, you can substitute Visual C++. But you'll easily save the equivalent of the purchase price of VB4 in time when you use it as your OLEISAPI development tool.

Additional Tools

Several tools, although not absolutely necessary, are helpful to the OLEISAPI developer. These either come with VB4 or are standard issue in NT 4.x and include:

Your VB4 application and OLEISAPI.DLL need several runtime files. The common files needed for most VB4 OLEISAPI projects are as follows:

File Name

Needed by

ven2232.olb

Setup program

olepro32.dll

Setup program

msvcrt20.dll

Setup program

msvcrt40.dll

Setup program

ctl3d32.dll

Setup program

MFC40.DLL

Average VB4 project

MSRDO32.DLL

VB4 project using RDO


OLEISAPI.CPP

The source code for OLEISAPI.DLL is supplied in the INETSDK. It is small and relatively simple with three primary functions as hooks into IIS. You should not normally have to change this code.

The three primary functions are as follows:

HRESULT GetClsidFromProgIdA(LPCLSID pclsid, CHAR* pszName,

long cbName)

This function gets the magic cookie class ID from the registry for a given OLE server program name.

BOOL CallObject(EXTENSION_CONTROL_BLOCK *pECB,

CHAR *pszProgid,

CHAR *pszMethod)

This is the heart of the OLEISAPI.DLL. It's the function that calls your OLE server.

void ErrorResponse(EXTENSION_CONTROL_BLOCK *pECB,

CHAR *pszProgid,

CHAR *pszMethod)

In the event of an error, this function tells the user (via generated HTML) that a problem occurred.

BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)

This is a necessary exported function that returns a rudimentary version report to IIS.

DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)

This exported callback function is called by IIS. It in turn invokes CallObject, which kicks off your OLEISAPI application.

The source code for OLEISAPI.CPP is on the companion CD to this book.

Configuring the Environment

For the following examples, we assume that IIS is installed in c:\winnt\system32\inetsrv and that the publication directories are in c:\inetpub. The directory structure holds at least the following:

Directory

Description

c:\winnt\system32\inetsrv\server

IIS service executables, etc.

c:\inetpub\wwwroot

HTML files residing here

c:\inetpub\scripts

Executable components (such as OLEISAPI.DLL)

Components in inetpub\scripts must have execute permission. You must set this capability in IIS Manager. Make sure the files have read and execute permission for the user you set up when installing IIS (usually IUSR_XXXX) in IE as well.

Components in inetpub\wwwroot must have read permission for the IUSR_XXXX account.

If permissions are not set correctly, you'll get errors hinting that components are missing or you'll have undefined problems. The error-reporting mechanism in OLEISAPI.DLL is rather basic.


When you run SETUP for your VB4 OLE server DLL, it is put in /Program Files/Common Files/OleSvr by default.

Creating a "Hello, World!" OLEISAPI Application

In this section, we create the ubiquitous "Hello World!" program as an OLEISAPI application.

We begin by creating a new project called testprj1.vbp. This project is composed of only two source files: testprj1.cls and modole.bas.

The Project Files

Start VB4 (32-bit edition). Remove the default Form1.frm from the project, and add one class module and one module.

From the Tools menu, select Options. Select the Project tab and amend the project name to TProject1. Select the StartMode: OLE server option and click OK (see Figure 11.2).

This is the setup for any OLEISAPI project and the basis of all samples in this chapter. The module has only a Main subroutine, as needed to create an OLE server (see Listing 11.1).

Fig. 11.2

The VB4.0 Project Options dialog.

In the module, enter the code in Listing 11.1.

Listing 11.1 OLEISAPI.BAS-Test Project 1: Code Module

Option Explicit

Sub Main()

End Sub

The real meat and potatoes of our OLEISAPI application is the class module and it is very small. Enter the code in Listing 11.2.

Listing 11.2 TCLASS1.CLS-Test Project 1: Class Module

Option Explicit

Public Sub HelloWorld(Request As String, Response As String)

Dim htmlHeader As String

htmlHeader = "ContentType: Text/HTML" & vbCrLf & vbCrLf

htmlHeader = htmlHeader & "<HTML><TITLE>Say Hello to the

World</TITLE>" & vbCrLf

Response = htmlHeader & "<BODY>Hello World</BODY></HTML>" &

vbCrLf

End Sub

With the code window for the class module open, right-click on it and select Properties. For Instancing, select 2 - Creatable Multiuse. For Name, amend to TClass1 and set Public to True (see Figure 11.3).

This is a standard configuration for OLE server class modules to be compiled to DLLs. The module created earlier can also be named according to personal preference. The name is not critical to this example.

Fig. 11.3

The VB4.0 Class Module Properties dialog.

Creatable Multiuse is for OLE server DLLs and means that all instances of the object share the same code block. If you want to make an OLE server EXE, your instancing property should be set to Creatable Singleuse.

But at this writing, NT 4.0 and IIS 3.0 were reporting an error in USER32.DLL when trying to call EXE OLE servers from IIS.


The advantage of EXE OLE servers over DLLs is that DLLs save memory by using a single code block that is shared among all instances. But this means that calls to the DLL must be serialized because VB4.0 cannot multithread an application.

OLE server EXEs do not share code because a new copy of the OLE server is loaded for each instance. So OLE server EXEs can multitask. The penalty for this is memory use of approximately 500K per instance.


These settings create the core of the OLEISAPI callable OLE server, which now has a class identifier of TProject1.TClass1 and a single method of HelloWorld.

From the File menu, select Make OLE DLL File. This creates your OLE Server and puts it in the registry.

If you need to recompile your DLL and it has been accessed by IIS, you'll get an ACCESS DENIED error and saying that the file is in use. To get around this, stop all IIS services (WWW, FTP, and gopher) on your computer.

This releases the DLL from use and allows you to replace the current version with the new one. Don't forget to restart the services afterwards.

How It Works

As you can see from the sample HelloWorld function, our OLEISAPI OLE server member function has this signature:

HRESULT SomeMethod(request as string, response as string)

All OLEISAPI callable member functions must have this signature.

When your function is called from a URL, the request string holds either the HTTP query string (in the case of a GET request) or the data in the HTTP request (in the case of a POST request). The response string should be set by your OLE server object to the HTTP response you want presented to the user.

The beginning of the response string should always be Content-Type:, followed by a valid MIME type.

URLs that invoke this service from a link look like this:

http://machine/path/oleisapi.dll/Project.class.method?param1=foo&param2=bar

In our case, it would be:

http://localhost/scripts/oleisapi.dll/TProject1.TClass1.HelloWorld

The additional ?param1= is not needed for this example. But if it were appended, it would be ignored. This information comes through as the request parameter in our method.

But since we do no checking on this value, it can be discarded. We'll use it in later projects.

Enter the above URL in your preferred browser. A page is returned displaying "Hello, World!" in your default font (see Figure 11.4).

Fig. 11.4

Output from "Hello, World!" application.

OLEISAPI.DLL also works with HTML forms that use the POST/GET methods. In this case, the parameters to the call come from the form elements rather than the URL. This is the direction for the next project.

OLEISAPI 2.0 Version

To use OLEISAPI 2.0 in this project, you need to add a reference. From the Tools menu, select References. In the References dialog box, select OLEISAPI2 Type Library. Click OK.

Change the source code in the class module to the following:

Public Sub HelloWorld(objRequest as Request)

objRequest.WriteResponse "<HTML><TITLE>Say Hello to the World</TITLE>" & vbCrLf

objRequest.WriteResponse "<BODY>Hello World</BODY></HTML>" & vbCrLf

End Sub

OLEISAPI 2.0 exposes one object, called Request. This object has a number of methods and properties, but we use only WriteResponse in this project. A full list of properties and methods is in the README.TXT file that accompanies OLEISAPI2.DLL on the CD.

Change the URL to

http://localhost/scripts/oleisapi2.dll/TProject1.TClass1.HelloWorld

This highlights the differences between the OLEISAPI and OLEISAPI 2.0 implementations of a simple project, even though the basic concept is the same.

A Personalized "Hello, World!" Application

The previous example produces a static output from a static URL-hardly an exciting prospect to publish on the Web. In this example, we ask users to enter their name in an HTML form. We analyze the result and return a dynamic page based on the user input.

The Project Files

Create a new VB project as before, but set the project name to TProject2. Add one class module and one module.

In the module, add the code in Listing 11.3.

Listing 11.3 OLEISAPI.BAS-OLEISAPI Code Module

Option Explicit

Sub Main()

End Sub

Private Function GetField(Info As String, FieldName As String) As

String

Dim Position1 As Integer, Position2 As Integer, TempField As

String, Character As Integer

Position1 = InStr(Info, FieldName & "=")

If Position1 = 0 Then Exit Function

Position1 = Position1 + Len(FieldName) + 1

Position2 = InStr(Position1, Info, "&")

If Position2 = 0 Then Position2 = Len(Info) + 1

TempField = Mid(Info, Position1, Position2 - Position1)

Position1 = InStr(TempField, "%")

Do Until Position1 = 0

Character = Decimal(Mid(TempField, Position1 + 1, 2))

TempField = Left(TempField, Position1 - 1) & Chr(Character)

& Right(TempField, Len(TempField) - Position1 - 2)

Position1 = InStr(Position1 + 1, TempField, "%")

Loop

Position1 = InStr(TempField, "+")

Do Until Position1 = 0

TempField = Left(TempField, Position1 - 1) & " " &

Right(TempField, Len(TempField) - Position1)

Position1 = InStr(TempField, "+")

Loop

GetField = TempField

End Function

Private Function Decimal(sHEX As String) As Integer

Dim iHigh As Integer, iLow As Integer

iHigh = InStr("0123456789ABCDEF", Left(sHEX, 1)) - 1

iLow = InStr("0123456789ABCDEF", Right(sHEX, 1)) - 1

Decimal = iHigh * 16 + iLow

End Function

In the class module, enter the code in Listing 11.4.

Listing 11.4 TCLASS2.CLS-Test Project 2: Class Module

Option Explicit

Public Sub SubmitNames(Request As String, Response As String)

Dim htmlHeader As String

htmlHeader = "ContentType: Text/HTML" & vbCrLf & vbCrLf

htmlHeader = htmlHeader & "<HTML><TITLE>Say Hello to the World

in Person</TITLE>" & vbCrLf

htmlHeader = htmlHeader & "<BODY>" & vbCrLf

If GetField(Request, "FirstName") = "" Then

Response = Response & "First Name not specified<BR>" &

vbCrLf

Else

Response = Response & "First Name :" & GetField(Request,

"FirstName") & "<BR>" & vbCrLf

End If

If GetField(Request, "LastName") = "" Then

Response = Response & "Last Name not specified<BR>" & vbCrLf

Else

Response = Response & "Last Name :" & GetField(Request,

"LastName") & "<BR>" & vbCrLf

End If

Response = htmlHeader & Response & "</BODY></HTML>"

End Sub

Set the properties of the new class and name it TClass2.

In this class, there is one public method, SubmitNames. The module now holds two private methods, GetField and Decimal.

The SubmitNames method is called by our HTML form. The first few lines create a standard HTML header. The line

If GetField(Request, "FirstName") = "" Then

calls one of the private GetField functions. This function extracts fields from the request string. For example, the request parameter on the SubmitNames function might be FirstName=Dean&LastName=Cleaver. This is the ?param1=foo&param2=bar section of the URL without the "?".

The command GetField(Request, "FirstName") searches the request parameter for "FirstName=". It returns the value between that and the next "&" or the end-of-file (EOF), whichever comes first. In the example above, it would return Dean.

The GetField function also looks for "%" signs and "+" signs. If our HTML form had a Name entry box and we entered Dean Cleaver!, the request string would be Name=Dean+Cleaver%21. The space has been changed to a "+", and the "!" has been changed to "%21".

These conversions ensure cross-platform compatibility and protect the integrity of the data. The "%" signifies HEX, 0x21 = 33 decimal, which is the ASCII code for "!".

The Decimal function takes a two-character HEX string ("21") and converts it to decimal (33), which can then be returned as a character. Thus GetField would return Dean Cleaver! correctly.

We write the GetField function in a generic format so it can be included in all projects of this type. Other projects in this chapter need this code, along with the Decimal function.

The HTML Form

For this example, we create an HTML form. In Notepad (or your preferred editor), enter the text in Listing 11.5. Take note of the exact format of the text in the listing.

Listing 11.5 TFORM2.HTM-Test Form 2: HTML

<HTML>

<HEAD>

<TITLE>Say Hello in Person</TITLE>

</HEAD>

<BODY>

<FORM ACTION="/scripts/oleisapi.dll/TProject2.TClass2.submitnames"

METHOD="GET" ENCTYPE="application/x-www-form-urlencoded">

<P>

First Name:<INPUT NAME="FirstName" VALUE="" MAXLENGTH="25" SIZE=25>

<P>

Last Name:<INPUT NAME="LastName" VALUE="" MAXLENGTH="25" SIZE=25>

<P>

<INPUT TYPE=SUBMIT VALUE="Submit" NAME="Submit">

</FORM>

</BODY>

</HTML>

Save this as TForm2.htm in your www publication directory, which is c:\inetpub\wwwroot by default in a fresh NT 4.0 install.

Open your browser and enter the URL

http://localhost/TForm2.htm

You'll see a simple HTML form, as shown in Figure 11.5.


Fig. 11.5

The TForm2.htm Web page.

Enter your first and last name, and click Submit. The form should look like Figure 11.6.

Fig. 11.6

The output from Test Project 2.

Note the URL in the browser window:

http://localhost/scripts/oleisapi.dll/TProject2.TClass2.submitnames?FirstName=Dean&LastName=Cleaver&Submit=Submit

For OLEISAPI applications, the only difference between the GET and POST methods of form actions is the display of the parameters in the browser window. To highlight this point, edit the TForm2.htm file. Change METHOD= GET to METHOD= POST.

Save the file, refresh the form in the browser, and re-submit the information. The URL should now be displayed only as

http://localhost/scripts/oleisapi.dll/TProject2.TClass2.submitnames

In most cases, this is preferable to displaying the full URL. However, the extra information can be useful for debugging and testing.

OLEISAPI 2.0 Version

To convert this sample to OLEISAPI 2.0, we use a new property. Instead of receiving Request as a parameter of the subroutine, we retrieve it from the objRequest.QueryString property, as follows:

Resuest = objRequest.QueryString

You could also convert this project to OLEISAPI 2.0 by putting this line at the top of the routine, changing the routine parameters, using the WriteResponse method, adding the reference, and changing the URL in the form. We leave this for you to do as an exercise.

A Sample Guest Book Application

In the previous sample, we analyze the input and return it to the user in a new HTML page-an interesting step but hardly useful in the real world. In this sample, we gather the information supplied by the user, store it in a guest- book database, and thank the user for the input.

The Project Files

Create a new VB project as before but set the project name to TProject3. Add one class module and one module. The module is identical to the one in the previous example, so you may want to include that file rather than create a new one.

In the class module enter the code in Listing 11.6.

Listing 11.6 TCLASS3.CLS-Test Project 3: Class Module

Option Explicit

Public Sub GuestBook(Request As String, Response As String)

Dim htmlHeader As String, htmlFooter As String, sSQL As String,

dbGuestBook As Database

htmlHeader = "ContentType: Text/HTML" & vbCrLf & vbCrLf

htmlHeader = htmlHeader & "<HTML><TITLE>Guestbook Application</

TITLE>" & vbCrLf

htmlHeader = htmlHeader & "<BODY>" & vbCrLf

htmlFooter = "</BODY></HTML>"

sSQL = "insert into GuestBook (Title, GivenNames, Surname,

Address, Country, VB, VC, Delphi) values ("

If GetField(Request, "Title") = "" Then

Response = htmlHeader & "Please specify your title<BR>" &

vbCrLf & htmlFooter

Exit Sub

Else

Response = htmlHeader & "Thank you for registering in my

guestbook.<BR>Your Guestbook details are as follows:<BR>" &

vbCrLf

If GetField(Request, "Title") = "Other" Then

Response = Response & GetField(Request, "Other") & " "

sSQL = sSQL & "'" & GetField(Request, "Other") & "', "

Else

Response = Response & GetField(Request, "Title") & " "

sSQL = sSQL & "'" & GetField(Request, "Title") & "', "

End If

End If

If GetField(Request, "GivenNames") = "" Then

Response = htmlHeader & "Please specify your given

names<BR>" & vbCrLf & htmlFooter

Exit Sub

Else

Response = Response & GetField(Request, "GivenNames") & " "

sSQL = sSQL & "'" & GetField(Request, "GivenNames") & "', "

End If

If GetField(Request, "Surname") = "" Then

Response = htmlHeader & "Please specify your surname<BR>" &

vbCrLf & htmlFooter

Exit Sub

Else

Response = Response & GetField(Request, "Surname") & "<BR>"

& vbCrLf

sSQL = sSQL & "'" & GetField(Request, "Surname") & "', "

End If

If GetField(Request, "Address") = "" Then

Response = htmlHeader & "Please specify your address<BR>" &

vbCrLf & htmlFooter

Exit Sub

Else

Response = Response & GetField(Request, "Address") & "<BR>" & vbCrLf

sSQL = sSQL & "'" & GetField(Request, "Address") & "', "

End If

Response = Response & GetField(Request, "Country") & "<BR>" &

vbCrLf

sSQL = sSQL & "'" & GetField(Request, "Country") & "', "

If GetField(Request, "PL1") = "VB" Then

Response = Response & GetField(Request, "PL1") & "<BR>" &

vbCrLf

sSQL = sSQL & "-1, "

Else

sSQL = sSQL & "0, "

End If

If GetField(Request, "PL2") = "VC" Then

Response = Response & GetField(Request, "PL2") & "<BR>" &

vbCrLf

sSQL = sSQL & "-1, "

Else

sSQL = sSQL & "0, "

End If

If GetField(Request, "PL3") = "Delphi" Then

Response = Response & GetField(Request, "PL3") & "<BR>" &

vbCrLf

sSQL = sSQL & "-1)"

Else

sSQL = sSQL & "0)"

End If

Response = Response & htmlFooter

On Error Resume Next

Set dbGuestBook = OpenDatabase("c:\data\projects\TProj3\

GuestBK.mdb")

dbGuestBook.Execute sSQL, dbFailOnError

If Err = 0 Then Exit Sub

Response = htmlHeader & "I'm sorry. Your details could not be

saved. Please check them and try again later." & vbCrLf &

htmlFooter

End Sub

The program assumes that an access database named GuestBK.mdb is in a C:\data\projects\tproj3\ directory. This can be changed to suit your needs (to a FoxPro table or ODBC data source, for example). But it's up to you to open the new database correctly.

The database has eight fields, as shown in Table 11.1.

Table 11.1 Guest-Book Table Definition

Field Name

Data Type

Title

Text

GivenNames

Text

Surname

Text

Address

Text

Country

Text

VB

Yes/No (Boolean or Integer)

VC

Yes/No

Delphi

Yes/No

This sample analyzes the returned string in much the same way as the previous example. But it returns an error to the user if one of the text entries is not filled out.

As it checks each field, this sample is also compiling a standard query language (SQL) insert statement, which is executed just before the end of the subroutine. If there is an error in saving to the database, the user is informed of an a error and told to try again.

Typical errors occur when a user enters O'Rielly as a surname, for example. The SQL statement would be "…, 'O'Rielly', …" which causes an error. But it is beyond the scope of this chapter to discuss and develop workarounds for this type of error.

The HTML Form

We need a fairly complex HTML form to test this project. In Notepad (or your preferred editor), enter the code in Listing 11.7.

Listing 11.7 TFORM3.HTM-Test Form 3: HTML

<HTML>

<HEAD>

<TITLE>Register in my Guest Book</TITLE>

</HEAD>

<BODY>

<FORM ACTION="/scripts/oleisapi.dll/TProject3.TClass3.GuestBook"

METHOD="POST" ENCTYPE="application/x-www-form-urlencoded">

<P>

Surname:<INPUT NAME="Surname" VALUE="" MAXLENGTH="25" SIZE=25>

<P>

Given Names:<INPUT NAME="GivenNames" VALUE="" MAXLENGTH="25" SIZE=25>

<P>

Title:<INPUT TYPE="RADIO" NAME="Title" VALUE="Mr">Mr <INPUT TYPE=

"RADIO" NAME="Title" VALUE="Mrs">Mrs

<INPUT TYPE="RADIO" NAME="Title" VALUE="Miss">Miss <INPUT TYPE=

"RADIO" NAME="Title" VALUE="Ms">Ms

<INPUT TYPE="RADIO" NAME="Title" VALUE="Other">Other <INPUT NAME=

"Other" VALUE="" MAXLENGTH="10" SIZE=10>

<P>

Address:

<TEXTAREA NAME="Address" ROWS=2 COLS=50>

</TEXTAREA>

<P>

Country:<SELECT NAME="Country" >

<OPTION VALUE="USA">USA

<OPTION SELECTED VALUE="NZ">New Zealand

<OPTION VALUE="Other">Other</SELECT>

<P>

Programming Languages:

<P>

<INPUT TYPE=93"CHECKBOX" NAME="PL1" VALUE="VB">Visual Basic

<P>

<INPUT TYPE="CHECKBOX" NAME="PL2" VALUE="VC">Visual C++

<P>

<INPUT TYPE="CHECKBOX" NAME="PL3" VALUE="Delphi">Delphi

<P>

<INPUT TYPE=SUBMIT VALUE="Submit" NAME="Submit"> <INPUT TYPE=

RESET VALUE="Reset">

</FORM>

</BODY>

</HTML>

Save this file to your www publishing directory and call it TForm3.htm.

Enter the following URL in your browser:

http://localhost/tform3.htm

You see the HTML form shown in Figure 11.7.

Fig. 11.7

The TForm3.htm Web page.

To submit this form, fill in all the text boxes (the Other box is not needed). Optionally select a Country and the languages that you program in. A sample HTML page from the project is shown in Figure 11.8.

Fig. 11.8

The output from Test Project 3.

OLEISAPI 2.0 Version

To convert the form to OLEISAPI 2.0, you would need a similar change to the project just discussed. The base methods and properties are the same.

A Sample Database Query Application

This sample queries a database and returns the results to the user. In this case, the results are links to physical documents (.DOC or .XLS files) on the server in an IIS virtual directory called /Bulletins.

This sample is a shortened version of a project developed for BMW New Zealand Limited. It is reproduced in part with their kind permission.

The Project Files

Create a new VB project as before but set the project name to TProject4. Add one class module and one module. With one addition, the module is identical to the one in the previous example, so you may want to include that file rather than create a new one or make a copy of it.

Add the new code shown in Listing 11.8.

Listing 11.8 OLEISAPI.BAS-OLEISAPI Code Module

Private Function NoSpaces(Info As String) As String

Dim tmpString As String, Position1 As Integer

tmpString = Info

Position1 = InStr(tmpString, " ")

Do Until Position1 = 0

tmpString = Left(tmpString, Position1 - 1) & "+" &

Right(tmpString, Len(tmpString) - Position1)

Position1 = InStr(tmpString, " ")

Loop

NoSpaces = tmpString

End Function

In the class module, enter the code shown in Listing 11.9.

Listing 11.9 TCLASS4.CLS-Test Project 4: Class Module

Public Sub GetBulletins(Request As String, Response As String)

On Error Resume Next

Dim Information As String

Dim HTMLHeader As String

Dim FileName As String

Dim FileDate As Date

Dim FileTopic As String

Dim Position1 As Integer

Dim Position2 As Integer

Dim Position3 As Integer

Dim Result As String

Dim Spacer As String

Dim dbBulletins As Database

Dim rsBulletins As Recordset

Dim Counter As Integer

Dim I As Integer

Const iNumberOfBulletins = 7 ' Limits the number of lines

displayed

Set dbBulletins = Workspaces(0).OpenDatabase("c:\data\project

\tproj4\bulletin.mdb")

HTMLHeader = "Content-type: text/html" & vbCrLf

If Request <> "" Then

If InStr(UCase(Request), "TOPIC") <> 0 Then

Result = GetField(Request, "TOPIC")

Set rsBulletins = dbBulletins.OpenRecordset("SELECT

date,description,filename FROM Bulletin WHERE bulletin

level like '" & Result & "%' ORDER BY date DESC",

dbOpenSnapshot)

Response = Response & HTMLHeader & vbCrLf

Response = Response & "<TITLE>" & GetField(Request,

"TITLE") & "</TITLE>" & vbCrLf

Response = Response & "<BODY BGCOLOR=FFFFFF>" & vbCrLf

Response = Response & "<P>" & vbCrLf

Response = Response & "<TABLE>" & vbCrLf

Response = Response & Spacer

Result = GetField(Request, "BACK")

If InStr(UCase(Request), "START") <> 0 Then

For Counter = 1 To Val(GetField(Request, "START"))

rsBulletins.MoveNext

Next

Counter = 0

Do Until rsBulletins.EOF

Response = Response & "<TR><TD WIDTH=100><A

HREF=/Bulletins/" & rsBulletins!FileName & ">" &

rsBulletins!Date & "</A></TD>" & vbCrLf

Response = Response & "<TD WIDTH=1000><A HREF=

/Bulletins/" & rsBulletins!FileName & ">" &

rsBulletins!Description & "</A></TD></TR>" & vbCrLf & vbCrLf

rsBulletins.MoveNext

Counter = Counter + 1

If Counter = iNumberOfBulletins Then

Response = Response & "</TABLE>" & vbCrLf

If rsBulletins.EOF Then

If Val(GetField(Request, "START")) <=

iNumberOfBulletins Then

Response = Response & "<TABLE

BORDER=0><TR><TD><FONT COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.getbulletins?Topic="

& GetField(Request, "Topic") & "&Back=" & Result & "&Title="

& NoSpaces(GetField(Request, "TITLE")) &

">...Previous</A></TD>"

Else

Response = Response & "<TABLE

BORDER=0><TR><TD><FONT COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.getbulletins?Topic=" &

GetField(Request, "Topic94") & "&Back=" & Result & "&Start=" &

Val(GetField(Request, "START")) - iNumberOfBulletins &

"&Title=" & NoSpaces(GetField(Request, "TITLE")) &

">...Previous</A></TD>"

End If

Else

If Val(GetField(Request, "START")) <=

iNumberOfBulletins Then

Response = Response & "<TABLE

BORDER=0><TR><TD><FONT COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.getbulletins?Topic=

" & GetField(Request, "Topic") & "&Back=" & Result &

"&Title=" & NoSpaces(GetField(Request, "TITLE")) & ">...

Previous</A></TD>"

Else

Response = Response & "<TABLE

BORDER=0><TR><TD><FONT COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.getbulletins?Topic="

& GetField(Request, "Topic") & "&Back=" & Result & "&Start="

& Val(GetField(Request, "START")) - iNumberOfBulletins &

"&Title=" & NoSpaces(GetField(Request, "TITLE")) & ">...

Previous</A></TD>"

End If

Response = Response & "<TD><FONT COLOR=#FFFFFF>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</TD>"

Response = Response & "<TD><FONT COLOR=

#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.

getbulletins?Topic=" & GetField(Request, "Topic") & "&Back="

& Result & "&Start=" & Val(GetField(Request, "START")) +

iNumberOfBulletins & "&Title=" & NoSpaces(GetField(Request,

"TITLE")) & ">More...</A></TD></TR></TABLE>"

End If

Exit Do

End If

Loop

If Counter < iNumberOfBulletins Then

For I = Counter To iNumberOfBulletins - 1

Response = Response & "<TR><TD WIDTH=

100>&nbsp </TD>" & vbCrLf

Response = Response & "<TD WIDTH=

1000></TD></TR>" & vbCrLf & vbCrLf

Next

Response = Response & "</TABLE>" & vbCrLf

If Val(GetField(Request, "START")) <=

iNumberOfBulletins Then

Response = Response & "<TABLE BORDER=

0><TR><TD><FONT COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.getbulletins?Topic="

& GetField(Request, "Topic") & "&Back=" & Result &

"&Title=" & NoSpaces(GetField(Request, "TITLE")) &

">...Previous</A></TD>"

Else

Response = Response & "<TABLE BORDER=

0><TR><TD><FONT COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.tclass4.getbulletins?Topic=" &

GetField(Request, "Topic") & "&Back=" & Result & "&Start="

& Val(GetField(Request, "START")) - iNumberOfBulletins &

"&Title=" & NoSpaces(GetField(Request, "TITLE")) &

">...Previous</A></TD>"

End If

End If

Response = Response & "</TABLE>" & vbCrLf

Else

Do Until rsBulletins.EOF

Response = Response & "<TR><TD WIDTH=100><A

HREF=/Bulletins/" & rsBulletins!FileName & ">" &

rsBulletins!Date & "</A></TD>" & vbCrLf

Response = Response & "<TD WIDTH=1000><A

HREF=/Bulletins/" & rsBulletins!FileName & ">" &

rsBulletins!Description & "</A></TD></TR>" & vbCrLf & vbCrLf

rsBulletins.MoveNext

Counter = Counter + 1

If Counter = iNumberOfBulletins Then

Response = Response & "</TABLE>" & vbCrLf

If Not rsBulletins.EOF Then

Response = Response & "<TABLE

BORDER=0><TR><TD><FONT COLOR=#FFFFFF>...Previous</TD>"

Response = Response & "<TD><FONT COLOR=#FFFFFF>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</TD>"

Response = Response & "<TD><FONT

COLOR=#FF0000><A HREF=/scripts/oleisapi.dll/tproject4.

tclass4.getbulletins?Topic=" & GetField(Request, "Topic") &

"&Back=" & Result & "&Start=" & Val(GetField(Request,

"START")) + iNumberOfBulletins & "&Title=" & NoSpaces

(GetField(Request, "TITLE")) & ">More...</A></TD></TR>"

End If

Exit Do

End If

Loop

If Counter < iNumberOfBulletins Then

For I = Counter To iNumberOfBulletins - 1

Response = Response & "<TR><TD WIDTH=

100>&nbsp </TD>" & vbCrLf

Response = Response & "<TD WIDTH=

1000></TD></TR>" & vbCrLf & vbCrLf

Next

Response = Response & "</TABLE>" & vbCrLf

Response = Response & "<TABLE BORDER=

0><TR><TD><FONT COLOR=#FFFFFF>...Previous</TD>"

End If

Response = Response & "</TABLE>" & vbCrLf

End If

End If

End If

Close

End Sub

The sample expects a database called Bulletin.mdb to be in the c:\oleisapi\samples\four\ directory. This database holds a Bulletins table with the fields shown in Table 11.2.

Table 11.2 Bulletins Table Field Listing

Field Name

Data Type

Date

Date/Time

Description

Text

FileName

Text

Bulletin_Level

Text

Some typical entries are shown in Table 11.3.

Table 11.3 Bulletins Table Sample Records

Date

Description

FileName

Bulletin_Level

31/7/96

3 Series Prices

3series.xls

Prices

2/8/96

5 Series Prices

5series.xls

Prices

31/7/96

3 Series Options

3seropts.doc

Options

How It Works

The first lines declare the variables needed. They verify that a valid request is passed and that it has a topic field. If this is valid, the database is queried for the given topic, as follows:

Result = GetField(Request, "TOPIC")

Set rsBulletins = dbBulletins.OpenRecordset("SELECT date,

description,filename FROM Bulletins WHERE bulletin_level like '

" & Result & "*' ORDER BY date DESC", dbOpenSnapshot)

If the topic were Prices, the first two records would be returned, as shown in Table 11.3, above.

Result = GetField(Request, "BACK")

This line checks for a BACK parameter, which is used to set the URL for a Back button.

The full version of this code includes an image complete with a client-side image map. This is updated to include the BACK parameter if it is passed. This URL is also passed to subpages created by this DLL. But is not used in this example without the image map.


If InStr(UCase(Request), "START") <> 0 Then

This line checks for a START value in the request. If present, this value means that more than seven entries were retrieved last time and that the More option was selected.

The limitation of seven was chosen so as not to display scroll bars in the browser. It is not a limitation of the program.

The number of lines returned (seven) is set by a constant at the top of the listing. If a START value is found, the record set is advanced to the starting position.

Response = Response & "<TR><TD WIDTH=100><A HREF=/Bulletins/" &

rsBulletins!FileName & ">" & rsBulletins!Date & "</A></TD>"

& vbCrLf

Response = Response & "<TD WIDTH=1000><A HREF=/Bulletins/" &

rsBulletins!FileName & ">" & rsBulletins!Description &

"</A></TD></TR>" & vbCrLf & vbCrLf

These two lines form one line of a two-field table. Included are HREF links to the /Bulletins virtual directory and to the file name as it is in the database. Fixed table widths are used to suit the client browser.

If Counter = iNumberOfBulletins Then

This line checks to see if the current list of items has reached the maximum limit for the page. If it has, you may need to add a More… link. The following psuedocode explains the logic for this section:

if EndOfRecordset then

if "START" <= MaximumNumber

show "Previous…" with no "START"

else

show "Previous…" with "START" = "START" - MaximumNumber

endif

else

if "START" <= MaximumNumber

show "Previous…" with no "START"

else

show "Previous…" with "START" = "START" - MaximumNumber

endif

show MANY_XX's in White (not visible) for spacing

show "More…" with "START" = "START" + MaximumNumber

endif

If no START value is found, the code progresses in much the same way. But the pseudocode section shows a Previous… in white if a More… is needed. The More… includes the START parameter.

If Counter < iNumberOfBulletins Then

For I = Counter To iNumberOfBulletins - 1

Response = Response & "<TR><TD WIDTH=100>&nbsp </TD>" & vbCrLf

Response = Response & "<TD WIDTH=1000></TD></TR>" & vbCrLf

& vbCrLf

Next

Response = Response & "</TABLE>" & vbCrLf

Response = Response & "<TABLE BORDER=0><TR><TD><FONT COLOR=

#FFFFFF>...Previous</TD>"

End If

This checks that if fewer than seven records are returned, the table is filled with blank lines to ensure that the layout is consistent.

A number of the methods used for the HTML file returned were to ensure good visibility in IE 1.5 for Windows NT 3.51. They are not necessarily a good example of HTML layout.

Entering the URL

http://localhost/scripts/oleisapi.dll/TProject4.TClass4.GetBulletins?TOPIC=Prices

returns the Web page shown in Figure 11.9, given the previous sample table data.

Fig. 11.9

The TProj4 Web page.

In the full version, the URL is hardcoded into another page that has links to Prices, Options e.t.c. The URL also includes a link back to that page via the BACK parameter. This is because the previous page could be from anywhere:

&BACK=/somepage.htm

OLEISAPI 2.0 Version

Once again, the conversion for this project is the same as for the previous two. But the options don't stop there. With the extra features of OLEISAPI 2.0, you could add a lot more functionality.

The Request object allows you to retrieve server variables, upload files, and do other such things that are not possible with OLEISAPI. Using some of this functionality, you could expand this project to include, for example, submitting the documents as well as viewing them. I leave this for you to investigate.

From Here...

Building and running OLEISAPI applications is straight-forward once you have all the components installed and configured. You must install and configure IIS, a browser, the OLEISAPI.DLL file and its files, and your OLE server application.

The complexity of your OLEISAPI application is limited only by your creativity and VB4 capabilities. These examples should give you a good grounding in OLEISAPI programming. You'll be able to produce bigger and better things in a very short time.

Topics covered in this chapter include:

From here, you can expand these basic samples for almost any application suitable for the Internet, for example:


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.