CHAPTER 4
Application Object



In the context of Active Server Pages, an application is the sum of all the files that
can be accessed through a given virtual directory and its subdirectories. This ASP
application context is the same for all clients using the application. For example, a
client from Thailand who requests pages from your /SearchApp virtual directory is
accessing the same “application” as a second client from Sweden who is
requesting pages from the same virtual directory—regardless of which specific
web page within the virtual directory each is requesting.

Just as traditional standalone applications allow you to share information
throughout the application, so too do ASP applications. You can share information
among all clients of a given ASP application using the Application object. This
built-in object represents the ASP application itself and is the same regardless of
the number or type of clients accessing the application and regardless of what part
or parts of the application those clients are requesting.

The Application object is initialized by IIS the moment the first client requests any
file from within the given virtual directory. It remains in the server’s memory until
either the web service is stopped or the application is explicitly unloaded from the
web server using the Microsoft Management Console.

IIS allows you to instantiate variables and objects with application-level scope.
This means that a given variable contains the same value for all clients of your
application. You also can instantiate server-side objects with application-level
scope that likewise contain the same values for all clients. These application-level
variables and objects can be accessed and changed from the context of any user’s
session and from any file within the current application.

As stated earlier, the Application object’s initialization occurs when the first user of
your application requests any file from within the virtual directory that the ASP
application encompasses. This initialization can be thought of as setting aside
memory for the given ASP application. The web server instantiates and initializes
the Application object for you. However, you can customize this initialization by
including code in a special optional file called GLOBAL.ASA. Although I will
discuss this file in greater depth in Chapter 10, Preprocessing Directives, Server-Side
Includes, and GLOBAL.ASA, it is worth presenting a brief overview here.

The GLOBAL.ASA file exists—if it exists—at the root of the physical directory
mapped to by your ASP application’s virtual directory. It is processed every time a
new user requests a page from within the application’s virtual directory. This file
contains initialization code for both the user’s session and the application itself. If
the user is not the first user, the application-specific sections of GLOBAL.ASA are
not processed. If the GLOBAL.ASA file does not exist or does not contain any
code, but the user’s request is the web server’s first request for files within a given
application, the web server still initializes the Application object. However, the
web server’s initialization involves only the dimensioning of memory required for
the application.

The GLOBAL.ASA file provides a place for you to create variables and objects that
have application-level scope. This section of the GLOBAL.ASA file represents an
event procedure. The event is the OnStart event, and its event handler is executed
when the application is started. It’s important to note that although the GLOBAL.ASA
file is processed for every user that makes a request, the Application object’s
OnStart event is executed for only the first user. (The OnStart and the corresponding
OnEnd event procedures are covered in detail later in this chapter.)
Variables and objects with application-level scope have the same value for all
users at all times during the life of the application. If one user requests a page
containing code that changes an application-level variable’s value, then that variable’s
value is changed for all users. This presents a problem: potentially, two or
more users could attempt to change the value of the same application-level variable
at the same time. Fortunately, ASP provides the Application object’s Lock and
Unlock methods to avoid conflicts in these situations. Just as you must carefully
consider the ramifications of using global variables in a multithreaded application,
you also must consider the ramifications of using variables with application-level
scope. Use application-level variables with care.

The properties, collections, methods, and events of the ASP Application object are
outlined in the following box.


Comments/Troubleshooting
Application-level variables are, in effect, global variables for your ASP application.
The use of globals in ASP applications should be viewed with as much skepticism
as the use of globals in traditional standalone applications, if not with more. The
most important step is to painstakingly consider its scope before implementing any
object or variable with application-level scope. There are very few instances in
which using these ASP global variables is necessary.

With that warning, there are a few instances in which using application-level variables
or objects is useful in creating functional ASP applications. One of the most
important of these is maintaining application-specific statistics for your web site.

Using application-level variables that are incremented at the beginning of each
user session, for example, you could maintain a count of clients that have used
your application. Although such web management tools as Microsoft Site Server
perform similar tasks, their statistics are file specific, not application specific.
Some ASP literature has suggested using application-level objects for maintaining
open ActiveX Data Objects (ADO) database connections for all application users.

(For more information on ADO, see Chapter 11, ActiveX Data Objects 1.5.) This is
not a good use of application-level variables, since this approach prevents ODBC
from pooling connections per individual pages.* However, you could use an application-
level variable to maintain an application-specific connection string for that
same database connection.

There is one trap that you should be aware of when considering the use of application-
level variables and objects. Consider the following scenario. You have two
physical directories: c:\inetpub\wwwroot\MainApp and c:\inetpub\wwwroot\
MainApp\SearchApp. These directories are mapped to the virtual directories
/MainApp and /SearchApp, respectively. You have, in effect, an application within
an application. The first client requests a page within the c:\inetpub\wwwroot\
MainApp\SearchApp physical directory. Which initialization code will be used to
initialize the Application object—the code in the GLOBAL.ASA for /MainApp or the
GLOBAL.ASA for /SearchApp? In this case the /SearchApp GLOBAL.ASA is the one
processed. Until a file in /MainApp that does not exist in /SearchApp is requested,
the GLOBAL.ASA file for /MainApp is not processed. If the two GLOBAL.ASA files
define different sets of application-level variables, you have no way of knowing
within your code which Application variables were properly initialized without
testing them.


Application Object Summary

Properties
None

Collections
Contents
StaticObjects

Methods

Lock
Unlock

Events
OnStart
OnEnd

* ODBC connection pooling provides a method by which ODBC connections can be reused
by successive users. Instead of creating a new connection each time a client requests one, the
server attempts to reuse an already existing connection that is no longer in use. If unused
ODBC connections reside in memory after a certain period of time (configured in the MMC),
they are destroyed to free memory.


Finally, note that IIS now allows you to set ASP applications up in separate
memory spaces from each other and from the web server itself by simply checking
an option on the Properties panel of a given virtual directory in IIS’s Microsoft
Management Console. This ability is an important improvement in IIS. If your ASP
application is running in a separate memory space from the web server and a
server object in it (or the scripting engine itself) crashes, it will not also crash the
web server or your other ASP applications.

Collections Reference
Contents Collection
Application.Contents(Key)


The Contents collection of the Application object contains all the application-level
scoped variables and objects added to the current application through the use of
scripts (not through the use of the <OBJECT> tag).
Before examining how elements are added to the Contents collection, you must
first understand the properties of the Contents collection. The Contents collection
has three properties:

Item

Sets or retrieves the value of a specific member of the Contents collection.
You determine which specific member of the collection by using an index
number or a key. For example, if you wish to set the value of the first
element of the Contents collection, you could use a line of code similar to the
following:

Application.Contents.Item(1) = 3.14159

Note that you use a 1 (one), not a 0 (zero), to represent the first element in
the Contents collection. This is a subtle point, since using a zero in your code
will not result in an error; it will simply be ignored.

The next point to note is that we could have set the value of this element
using a name instead of a number, as in:

Application.Contents.Item("PI") = 3.14159

The name of the element (in this case “PI”) is its Key property (discussed next).
Item is the default property of the Contents collection, and the Contents
collection is the default collection of the Applications object. This means that
each of the following three lines of code is interpreted in exactly the same
manner in your application:

Application.Contents.Item(1) = 3.14159
Application.Contents(1) = 3.14159
Application(1) = 3.14159

as is each of these:

Application.Contents.Item("PI") = 3.14159
Application.Contents("PI") = 3.14159
Application("PI") = 3.14159

One final point: it is always safer to use the key rather than the index when
referencing the value of a specific element in the Contents collection, because
the index numbers for elements in the Contents collection begin from the first
application-scoped variable value set by any user of your application. With
application-scoped variables, determining which variable was set first (aside
from those in the GLOBAL.ASA file) can be problematic.

As mentioned earlier, the values of the elements in the collection that were
set in the Application_OnStart event in the GLOBAL.ASA file are set in the
order in which they exist in the event procedure’s code.


Key
Represents the name of a specific element in the Contents collection.
Remember from earlier that each element’s value is represented by the Item
property. Similarly, each element’s name is represented by its Key property.

If you do not know the name of a specific key, you can obtain it using its
ordinal reference. For example, assume that you want to learn the key name
for the third element in the collection and, subsequently, retrieve that
element’s value. You could use the following code:

strKeyName = Application.Contents(3)
strKeyValue = Application.Contents.Item(strKeyName)
If, on the other hand, you know that the third element’s key name is “STATE,”
you could simply use the following code to retrieve the value of that element:
StrKeyValue = Application.Contents.Item("STATE")

Count
Represents the total number of elements in the Contents collection.

Notes
You can initialize application-level variables and thus add elements to the
Contents collection in one of two ways. First, you can initialize Application variables
in the Application_OnStart event procedure in the GLOBAL.ASA file, as
Example 4-1 illustrates.

Example 4-1: Initializing Application-Level Variables in GLOBAL.ASA

' <<<<<<<<<<<<<<< FROM GLOBAL.ASA >>>>>>>>>>>>>>>>>>
' This code resides in the GLOBAL.ASA file at the
' root of the current application.
' See Chapter 10 for more details on the GLOBAL.ASA file.
Sub Application_OnStart
Application.Contents.Item(1) = "Georgia"
Application.Contents(2) = "Kentucky"
Application(3) = "Alabama"
Application.Contents.Item("STATE_FOURTH") = "California"
Application.Contents("STATE_FIFTH") = "Oregon"
Application("STATE_SIXTH") = "Washington"
End Sub

The code in Example 4-1 creates six application-scoped variables, thus adding six
elements to the Contents collection. Note that these variables will be instantiated
and initialized only at the start of the application, not upon every visit to the site
by subsequent users. These variables maintain the same values unless another
script changes them for all pages and for all users.

You also can create application-scoped variables and thus add elements to the
Contents collection inside any script on any page. Note, however, that any variables
created in this manner are created and maintained across the whole
application and all its users. Example 4-2 illustrates this method of initializing
application-scoped variables.

The code in Example 4-2 adds six more application-scoped variables to the application.
Note that these variables will be reinitialized every time a user requests the
page containing this code. To prevent this waste of processor power, it might be
better to perform this initialization using code similar to the following:

<%
' A more efficient example of the creation of an
' application-scoped variable.
If IsEmpty(Application.Contents.Item(13)) Then
Application.Contents(13) = "Texas"
End If
%>

This code creates a 13th application variable for the current application only if it
has not already been created.

The Contents collection supports the For Each and For…Next constructs for iterating
the collection, as Example 4-3 demonstrates.

Example 4-2: Initializing Application-Level Variables in a Server-Side Script

<%
' This code exists in the server-side section of a script
' on the web site.
Application.Contents.Item(7) = "Florida"
Application.Contents(8) = "Tennessee"
Application(9) = "Mississippi"
Application.Contents.Item("STATE_TENTH") = "New York"
Application.Contents("STATE_ELEVENTH") = "New Jersey"
Application("STATE_TWELFTH") = "Vermont"
%>

Example 4-3: Using For Each with the Contents Collection

<%
For Each strKey in Application.Contents
%>

The next item in Application's Contents collection<BR>
has <%= strKey %> as its key and
<%= Application.Contents(strKey) %>

Note, however, that the Contents collection does not support the Add or Remove
methods that are common with most collection objects. This makes planning
imperative, since variables given application scope stay resident until the web
server is stopped or the last user’s session times out.

If you add an object to the Application’s Contents collection, make sure that the
threading model for the object supports its use in an application scope; use of the
free-threaded model is recommended.

* For more on the use of various threading models in IIS server components, see Shelley Powers’ forthcoming book Developing ASP Components, published by O’Reilly & Associates.

To access an application-scoped object’s properties or methods, use an extension
of the syntax you saw earlier for accessing the value of an application-scoped variable,
as the following code fragment illustrates:

' In this example, assume you have an application-scoped Ad
' Rotator variable called MyAdRot.
' Accessing a property:
intBorder = Application.Contents("MyAdRot").Border
' Executing a method:
Application.Contents("MyAdRot").GetAdvertisement("Sched.txt")

If you intend to use a given object in a transaction using the Object-
Context object, do not give that object application or session scope.
Objects used in transactions are destroyed at the end of the transaction
and any subsequent reference to their properties or calls to their
methods will result in an error.

When adding an array to the Application object’s Contents collection, add the
entire array as a whole. When changing an element of the array, retrieve a copy of
the array, change the element, and then add the array to the Contents collection as
a whole again. The code in Example 4-4 demonstrates this.
as its value.
<P>
<%
Next %>

* Free-threaded applications allow multiple user processes to access the same instance of the
component simultaneously.


Example 4-4: Working with Arrays in the Contents Collection

<%
' Create an array variable and add it to Contents collection.
ReDim arystrNames(3)
arystrNames(0) = "Chris"
Example 4-3: Using For Each with the Contents Collection (continued)

StaticObjects

Application.StaticObjects(Key)

The StaticObjects collection contains all of the objects added to the application
through the use of the <OBJECT> tag. You can use the Item property (discussed
later) of the StaticObjects collection to retrieve properties of a specific object in the
collection. You also can use the Item property of the StaticObjects collection to
access a specific method of a given object in the collection.
You can add objects to this collection only through the use of the <OBJECT> tag in
the GLOBAL.ASA file, as in the following example:

<OBJECT RUNAT=Server SCOPE=Application ID=AppInfo2
PROGID="MSWC.MyInfo">
</OBJECT>

You cannot add objects to this collection anywhere else in your ASP application.
The StaticObjects collection, like other ASP collections, has the following
properties:

Item
Returns a reference to a specific element in the collection. To specify an item,
you can use an index number or a key.

arystrNames(1) = "Julie"
arystrNames(2) = "Vlad"
arystrNames(3) = "Kelly"
Application("arystrUserNames") = arystrNames
%>
The second name in the User Names array is
<%= Application("arystrUserNames")(1) %>
<BR>
<%
' Change an element of the array being held in the
' Contents collection.
ReDim arystrNames(3)
arystrNamesLocal = Application("arystrUserNames")
arystrNamesLocal(1) = "Mark"
Application("arystrUserNames") = arystrNamesLocal
' The second name is now Mark.
%>
Now, the second name in the User Names array is
<%= Application("arystrUserNames")(1) %>
<BR>
Example 4-4: Working with Arrays in the Contents Collection (continued)

Key
Returns the name of a specific element in the collection; the name is assigned
by the ID attribute of the <OBJECT> tag. For example, you could receive the
name of the first element in the collection like this:
objElement = Application.StaticObjects.Key(1)
Use the value of the Key property to retrieve the value of an element by
name. For example, suppose the first object in the StaticObjects collection is
named MyAdRotator. You could then use the following line of code to set (or
retrieve) the value of the Border property of that object:
strKey = Application.StaticObjects.Key(1)
Application.StaticObjects.Item(strKey).Border = 0


Count
The current number of elements in the collection.
For more imformation on the Item, Key, and Count properties of a
collection, see the section on the Contents collection of the Application
object, earlier in this chapter.

Example

' <<<<<<<<<<<<<<< FROM GLOBAL.ASA >>>>>>>>>>>>>>>>>>
' This code resides in the GLOBAL.ASA file at the root
' of the current application. The following <OBJECT>
' tag is processed only once for the current application.
' See Chapter 10 for more details on the GLOBAL.ASA file.
<OBJECT RUNAT=Server
SCOPE=Application
ID=AppInfo1
PROGID="MSWC.MyInfo">
</OBJECT>
<OBJECT RUNAT=Server
SCOPE=Application
ID=AppInfo2
PROGID="MSWC.MyInfo">
</OBJECT>
' <<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>
<%
' The following code initializes the AppInfo1 component.
' This initialization code can reside anywhere.
AppInfo1.PersonalName = "Gertrude Stein"
AppInfo1.PersonalAddress = "233 Main Street"
AppInfo2.PersonalName = "David Davidson"
AppInfo2.PersonalAddress = "19A West Avenue"
36 Chapter 4 – Application Object
Methods Reference
ASP in a Nutshell: A Desktop Quick Reference, eMatter Edition
Copyright © 2000 O’Reilly & Associates, Inc. All rights reserved.
' The following code uses the StaticObjects collection
' of the Application object to retrieve the value
' of the PersonalName property of both AppInfo1 and AppInfo2.
For Each objInfo In Application.StaticObjects
%>
The personal name is <BR>
<%= Application.StaticObjects(objInfo).PersonalName%><P>
<%
Next
%>
There are <%= Application.StaticObjects.Count %> items
in the Application's StaticObjects collection.
Notes
The StaticObjects collection allows you to access any object instantiated with
application-level scope through the use of an <OBJECT> tag. Objects instantiated
using the Server.CreateObject method are not accessible through this collection.
The nomenclature here can be a bit confusing. To reiterate: the StaticObjects
collection contains those server objects instantiated through the use of the
<OBJECT> tag, not through the CreateObject method of the Server object.
The StaticObjects example in the IIS 4.0 documentation by Microsoft suggests that
if you iterate through this collection, you will be able to reference each property.
This is somewhat misleading, as it suggests that the collection actually represents
all the properties of the objects rather than the objects themselves. If you want to
access the properties or methods of objects in the StaticObjects collection, you
must use the dot operator outside of the parentheses around the Key, followed by
the property or method name, as demonstrated in the preceding example.
Objects created in the GLOBAL.ASA file are not actually instantiated on the server
until the first time a property or method of that object is called. For this reason, the
StaticObjects collection cannot be used to access these objects’ properties and
methods until some other code in your application has caused them to be instantiated
on the server.
Do not give application or session scope to an object used in a transaction using
the ObjectContext object. Objects used in transactions are destroyed at the end of
the transaction, and any subsequent references to their properties or calls to their
methods will result in an error.


Methods Reference

Lock
Application.Lock


The Lock method locks the Application object, preventing any other client from
altering any variables’ values in the Contents collection (not just those variables
you alter before calling the Unlock method). The corresponding Unlock method is
used to release the Application object so other clients can again alter the Contents
collection variable values. If you fail to use the Unlock method, IIS will unlock the
variable automatically at the end of the current Active Server Pages script or upon
script timeout,* whichever occurs first.

Parameters
None

Example
<%
' This script exists on the second page of a
' multipage ASP application, so that users may
' or may not visit it. The example shows how you could
' see how many visitors the page has had.
' Assume that TotalNumPage2 starts at 0.
' Lock the Application object.
Application.Lock
intNumVisits = Application.Contents("TotalNumPage2")
intNumVisits = intNumVisits + 1
Application.Contents("TotalNumPage2") = intNumVisits
' Explicitly unlock the Application object.
Application.Unlock
' NOTE: Using the PageCnt.DLL would be a more
' efficient manner of doing this.
%>
<HTML>
<HEAD><TITLE>Home Page</TITLE></HEAD>
<BODY BGCOLOR = #ffffcc>
Welcome to our homepage. You are client number
<%= Application.Contents("TotalNumPage2")%> to our site. Thank
you for your patronage.
</BODY>
</HTML>

Notes
Any client connected to your web server can call a script that potentially could
alter the value of a variable in the Application Contents collection. For this reason,
it is a good idea to use the Lock and Unlock methods every time you reference or
alter a variable in the Contents collection. This prevents the possibility of a client
attempting to change a variable’s value when another client is resolving that variable’s
value.

* The ASP script timeout is adjustable through the Properties page of the web site using the
Microsoft Management Console. The default is 120 seconds.



Keep in mind that you cannot create a read-only variable by using a call to the
Lock method without a corresponding call to Unlock, since IIS automatically
unlocks the Application object.

You do not have to call the Lock and Unlock methods in the Application_OnStart
event procedure (see this chapter’s Events Reference for more about the
Application_OnStart event). The Application_OnStart event occurs only once
regardless of the number of sessions that are eventually initiated. Only the first
client request triggers the Application_OnStart event and, for that reason, only that
client can alter the value of the specific Application variable. Also, no other client
requests will be handled until the Application_OnStart code has completed.

Unlock
Application.Unlock


The Unlock method releases the application variables from a Lock method call.
Once Unlock has been called, other clients can again alter the values of the variables
in the Application Contents collection. If you call Lock and do not provide a
corresponding Unlock, IIS will automatically unlock the variables in the Application
Contents collection at the end of the current active server page or when the
script times out, whichever comes first.

Parameters
None

Example
See the example for Application.Lock.

Notes

See the notes for Application.Lock.

Events Reference

OnEnd
Application_OnEnd


The Application_OnEnd event is triggered when the ASP application itself is
unloaded from the web server (using the Microsoft Management Console) or when
the application is inadvertently stopped for some reason (i.e., the web service is
stopped on the web server). Application_OnEnd is called only once per application.
The code for this event procedure resides in the GLOBAL.ASA file and is
processed after all other code in the file. It is in the code for the Application_
OnEnd event that you will “clean up” after any application-scoped variables.

Parameters
None

Example
' <<<<<<<<<<<<<<< FROM GLOBAL.ASA >>>>>>>>>>>>>>>>>>
' This code resides in the GLOBAL.ASA file at the
' root of the current application. The following
' procedure is processed only once for the current
' application.
' See Chapter 10 for more details on the GLOBAL.ASA file.
<SCRIPT LANGUAGE="VBScript" RUNAT=Server>
Sub Application_OnEnd
' This code will run on the server when
' the application stops.
' This code saves the final count of an application
' use counter to a file.
Set filsysObj1 = _
CreateObject("Scripting.FileSystemObject")
Set tsObj1 = filsysObj1.CreateTextFile("c:\usrcount.txt", _
True)
tsObj1.WriteLine(Application.Contents("AppUserCount"))
tsObj1.Close
End Sub
</SCRIPT>
' <<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>

Notes
The use of the Application_OnEnd event is tricky. The Microsoft documentation
suggests that the OnEnd event is triggered when there are no longer any active
sessions. However, this is not the case. Only when the web service is interrupted
or when the administrator explicitly unloads the application from the web server’s
memory (using the MMC) is the OnEnd executed. You cannot assume that this
event will ever be called from your application without something going wrong or
direct intervention on your part. This is yet another reason to very carefully
consider the implications before using application-level variables of any kind.

You cannot use the Server object method MapPath (see Chapter 8, Server Object,
for more on the Server object) to map a relative or virtual directory to a physical
directory within the Application_OnEnd event procedure. Microsoft gives no
reason for this limitation, though it is likely a security-related control.

OnStart
Application_OnStart


The Application_OnStart event is triggered when the first client request is received.
Application_OnStart is called only once per application. The code for this event
procedure resides in the GLOBAL.ASA file and is processed before any other code
or object instantiation in the file.

Parameters

None

Example
' <<<<<<<<<<<<<<< FROM GLOBAL.ASA >>>>>>>>>>>>>>>>>>
' This code resides in the GLOBAL.ASA file at the
' root of the current application. The following
' procedure is processed only once for the current
' application.
' See Chapter 10 for more details on the GLOBAL.ASA file.
<SCRIPT LANGUAGE="VBScript" RUNAT=Server>
Sub Application_OnStart
' This code will run on the server when
' the application starts.
' This code retrieves the last final user count
' and uses it to initialize an Application
' variable.
Set filsysObj1 = CreateObject("Scripting.FileSystemObject")
Set tsObj1 = filsysObj1.OpenTextFile("c:\usrcount.txt", _
True)
Application.Contents("AppUserCount") = tsObj1.ReadAll
tsObj1.Close
End Sub
</SCRIPT>
' <<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>

Notes
The Application_OnStart event procedure, if it exists, is the first code run on the
server for a given Active Server Pages application. For this reason, it is the best
place to initialize application-level variables. No other code in your ASP application
is guaranteed to run.

Carefully consider the use of application-level variables. Every variable with application
scope that you dimension and initialize in the Application_OnStart event
continues to take up memory on the server until the end of the application.

Chapter 5