CGI Handler Utility Modules

Each CGIHandler project should contain some common modules: Util.bas, Attr.cls, and Dict.cls. There should also be an empty Main.bas.

Util.bas

Util.bas contains the following utility routines:

Public Function UrlEncodeString(ByVal str As String) As String
Public Function UrlDecodeString(ByVal inp As String) As String
Public Sub CenterForm(frm As Form, wnd As Long)
Public Sub FloatForm(f As Form, yesno As Integer)

The CGI Handler methods are given some of their parameters, and can return some of their results, using Form-URL-Encoded strings (the same format that HTTP uses to transfer form data). This format holds any number of name=value pairs separated by ampersand characters, like this:

name1=value1&name2=value2&name3=value3

The format has a 'quoting' convention for special characters. All ASCII space chars (' ') are converted to ASCII plus ('+') chars. Any chars less than or equal to ASCII space (' ') or greater than ASCII tilde ('~') are converted to their two-byte hex equivalent preceded by a percent ('%') char (for example, '%0A' for ASCII newline). The ampersand ('&'), plus ('+'), percent ('%'), and equal ('=') chars are also hex-encoded.

UrlEncodeString takes an arbitrary string as input and returns the same string Form-Url-Encoded as output. UrlDecodeString takes a Form-Url-Encoded string as input and returns the decoded string as output. CenterForm takes a VB form reference and a window handle (HWND); it centers the form over the given window, or on the desktop if the window handle is 0. FloatForm takes a VB form reference and a Boolean; if the Boolean is true, the form is made a TopMost window, and if false, the form is made a NonTopMost window.

Attr.cls

Attr.cls defines the attribute class Attr, with public data members 'Name' and 'Value' (both as String) and no methods. This utility class is private to each Bot project and not exposed to clients. The class has its Public variable set to False, and its Instancing mode variable set to Not Creatable.

Issues: If this class is exposed, clients can cause memory leakage by leaving dangling object references. However, this means that each CGI Handler project will probably want to have its own copies of the Util, Attr, and Dict modules, leading to code duplication.

Dict.cls

Dict.cls defines the dictionary class Dict, based on the VB Collection class. This utility class is private to each CGI Handler project and not exposed to clients. The class has its Public variable set to False, and its Instancing mode variable set to Not Creatable.

Issues: (same as for Attr) If this class is exposed, clients can cause memory leakage by leaving dangling object references. However, this means that each CGI Handler project will probably want to have its own copies of the Util, Attr, and Dict modules, leading to code duplication.

A collection contains named references to Objects. A collection class must support the standard methods Add, Remove, and Item, and the standard read-only property Count. Add puts an object in the collection, and Remove takes one out. Item returns a reference to an object in the collection using either its associated name or its 1-based index. Count returns the number of objects in the collection. A collection can be iterated over using the VB For Each ... Next construct, or using syntax like For i = 1 to dict.Count.

All the entries in a Dict are instances of Attr. The Dict class has no public data members.In addition to the standard collection methods, it has methods to clear all entries, to load itself from a Form-URL-Encoded string of name-value pairs, and to dump its current state to a Form-URL-Encoded string. The Dict class manages a private Collection object data member that is initialized when an instance is created, and cleaned up when an instance is destroyed (all references released, which is accomplished in VB by setting all object references to 'Nothing').

The public methods and properties of Dict look like this:

Public Function Add(ByVal nmstr As String, ByVal valstr As String) As Attr
Public Sub Remove(ByVal Index As Variant)
Public Function Item(ByVal Index As Variant) As Attr
Public Property Get Count()

Public Function HasEntry(ByVal nmstr As String) As Integer
Public Property Get Entries() As Collection
Public Sub Clear()
Public Sub LoadFromString(ByVal str As String)
Public Function DumpToString() As String

Add takes a name and value and adds a new Attr entry to the dictionary, using the name as a key, and returns a reference to the new Attr object. Remove takes either a numeric index or a key string and removes the associated Attr entry from the dictionary. Item takes either a numeric index or a key string and returns a reference to the associated Attr object, or the special value Nothing if not found. The Count property returns the current number of entries in the dictionary.

The HasEntry method returns True if the given key exists in the dictionary, else False. The Entries property returns the private collection held by the dictionary instance, so that the client can use the VB For..Next construct to iterate over the collection. If a client uses this property outside of a For..Next construct, the client must careful to set any references to the collection to Nothing when finished to avoid dangling object references. Clear removes all Attr objects from the dictionary. LoadFromString takes a Form-Url-Encoded string of name-value pairs, clears all current Attr entries, and then adds the new name-value pairs as Attr entries (the format is "name1=value1&name2=value2"). DumpToString returns a Form-Url-Encoded string holding the current state of the dicitionary as name-value pairs (the format is "name1=value1&name2=value2").

Here's an example of how to use a Dict in a program (all methods are called):

	Dim d as New Dict ' create new object
	Dim entry as Attr
	Dim s as String
	Dim i as Integer
	d.LoadFromString("one=foo&two=bar")
	s = d.DumpToString
	Debug.Print "initial state: '" & s & "'"
	For Each entry In d.Entries
	   Debug.Print entry.Name & "=" & entry.Value
	Next
	For i = 1 to d.Count
	   Debug.Print "[" & i & "]: " & entry.Item(i).Name & "=" & entry.Item(i)Value
	Next i
	d.Remove "two"
	d.Add "three", "twiddle"
	s = d.DumpToString
	Debug.Print "final state: '" & s & "'"
	d.Clear
	Set d = Nothing  ' release reference to object

When this code is run, you should see something like this in the debug window:

	initial state: 'one=foo&two=bar"
	one=foo
	two=bar
	[1]: one=foo
	[2]: two=bar
	final state: 'one=foo&three=twiddle'