ASP 3.0高级编程（九）

ASP编程指南

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（九）

2． 创建Variant数组
在Session和Application对象中创建和使用一个Variant数组来存储值的方法目前尚未讨论，在这里作为一个非常有用的技术进行讨论。正如已经看到的那样，一个Variant数据类型可以包含一个数组，而不仅仅是一个值。
一个数组只是在内存的一个连续区域中以指定的次序存储二进制值的一个长行。要安排Variant，需要指向首项，并给出有关大小和结构的信息，脚本引擎可以做余下的事情。
可在一个Variant变量中创建一维、二维或多维数组，然后把该数组分配给一个应用程序层或用户会话层的变量，并保证整个数组可在相应的地方使用。下面代码演示了一个简单的一维数组的使用技术：
Dim varArray(3)
varArray(0) = “This is a”
varArray(1) = “Variant array”
varArray(2) = “stored in the”
varArray(3) = “Session object”
Session(“Variant_Array”) = varArray
3． 应用程序和会话在何时启动和结束
在介绍ASP应用程序和会话如何进行工作时提到过这个内容。以最基本的术语概述如下：
· 当第一个用户请求应用程序作用域内（即Web网站的缺省根目录内），或者在该网站的一个子目录内的一个用户定义的虚拟应用程序的一个ASP网页时，启动该应用程序。在任何用户会话启动之前发生。
· 当任意用户第一次请求在缺省应用程序或一个虚拟应用程序内的一个ASP网页时，启动一个会话（如果还没一个活动的会话）。
· 当用户在会话指定的超时周期内没有下载一个ASP网页时，会话结束。超时时间可以在脚本代码中使用Session.Timeout属性进行设置，可在Properties对话框中对各个应用程序单独设置，或者通过Active Directory的IIS：部分修改IIS元数据库中的缺省值进行设置。调用Session.Abandon方法的一个网页完成执行以后，会话也会结束。
· 在一个应用程序中的最后一个活动会话结束以后，该应用程序立即结束。
4． ASP处理指令
正如在第1章中所看到的，可以把一条处理指令增加到一个ASP网页。处理指令可以根据需要包含一个以上的条目。可以在语句中使用的关键字及其说明如表3-10所示：
表3-10 ASP指令关键字及说明
指令关键字
说 明

LANGUAGE=”language_name”
设置该网页的缺省的脚本语言，如：

ENABLESESSIONSTATE=”True”|”Fasle”
当设置为“True”时，防止一个会话的cookie发送到浏览器，因此将不会创建新的Session对象，任何现有会话的内容将不再可用

CODEPAGE=”code_page”
设置该网页的代码页，如

LCID=”locale_identifier”
设置该网页的位置标识符，如

TRANSACTION=”transaction_type”
指明该网页文件在一个事务环境下运行。有效值是：
“Required”：如果已有可用的事务，脚本将在其中运行；如果没有可用的事务，启动一个新的事务。
“Requires_New”：脚本将初始化一个新的事务。
“Supported”：如果已有可用的事务，脚本将在其中运行；而且不启动一个新的事务。
“Not_Supported”：脚本将不运行于任何已有的事务中，并且不初始化一个新的事务。
在第18章中详细介绍事务。

在一个网页上只能允许有一条处理指令，并且应该放在第一行。在处理指令中可以包含不止一个这样的条目，但必须用空格进行分隔，等号两端不能有空格，例如：

3.3.4 活动的ASP Application对象
我们提供一些简单的网页，这些网页示范了使用过程中的ASP Application和Session对象。为了能够正常使用，必须把它们放到服务器上的一个虚拟应用程序内，并且把所提供的global.asa文件放到该应用程序的根目录中。最简单的办法是把global.asa文件放到缺省Web网站的根目录（缺省情况下是C:/InetPub/WWWRoot）中。
对任何已有的global.asa文件重命名是一个好办法，可以在以后对该文件进行恢复。
本书的所有例子文件都可以从我们的Web网站上得到，在例子的Chapter03子目录中还有本章的所有其余示例网页。
在Chapter03子目录中，Default.asp网页是一个简单的菜单，该菜单允许运行Application和Session示例网页，运行屏幕如图3-13所示：

图3-13 Default.asp运行屏幕
1． 显示Application集合的内容
单击第一个链接，打开名为show_application.asp的Application对象示例页面。它显示了虚拟应用程序当前的Application对象的内容，如图3-14所示：

图3-14 Application对象的内容屏幕
注意到ASPCounter对象是StaticObjects集合的一个成员（通过元素进行定义），但是其余部份（由Server.CreateObject实例化）是Contents集合的成员。
可以看到使用global.asa例子网页放到这些集合中的值，这在前面已经看到：

PROGID=”MSWC.Counters”>

...
...

(1) 遍历Contents集合的代码
为了遍历Contents集合，可使用一个For Each ... Next结构。集合中的每一项可以是一个简单的Variant类型变量、一个Variant数组或者一个对象的引用。因为需要对每种类型的值进行不同的处理，所以就不得不对每一个进行检查来判别其类型。
在VBScript中可使用VarType函数完成这个工作。这里使用IsObject和IsArray函数代替：
For Each objItem in Application.Contents
If IsObject(Application.Contents(objItem)) Then
Response.Write “Object reference: ‘” & objItem & “’
”
ElseIf IsArray(Application.Contents(objItem)) Then
Response.Write “Array: ‘” & objItem & “’ contents are:
”
VarArray = Application.Contents(objItem)
‘Note: the following only works with a one-dimensional array
For intLoop = 0 To UBound(varArray)
Response.Write “ Index(“ & intLoop & “) = “ & _
VarArray(intLoop) & “
”
Next
Else
Response.Write “Variable: ‘” & objItem & “’ = “ _
& Application.Contents(objItem) & “
”
End If
Next
注意程序如何从Application对象检索该数组。将其分配给一个局部（Variant）变量，使用下面的语句：
varArray = Application.Contents(objItem)
使用UBound函数可以查找出数组的大小（元素的数量），这个值可以作为遍历的终止条件：
For intLoop = 0 UBound(varArray)
这个例子是一维数组，并将只显示这样的一个数组的内容。可根据需要编辑代码以处理多维数组，例如：
For intLoop = 0 To UBound(varArray)
IntNumberOfDimensions = UBound(varArray, 1)
For intDimension = 0 To intNumberOfDimensions
Response.Write “ Index(“ & intLoop & “) = “ _
& varArray(intLoop, intDimension)
Next
Response.Write “
”
Next
(2) 遍历StaticObjects集合的代码
StaticObjects集合包含了所有在global.asa中使用元素声明的对象引用。因为每个条目都是一个对象变量，可用简单些的代码对这个数组进行遍历。我们将输出对象的名字（在ID属性中原有的定义）：
For Each objItem in Application.StaticObjects
If IsObject(Application.StaticObjects(objItem)) Then
Response.Write “element: ID=’” & objItem & “’
”
End If
Next


ASP 3.0高级编程（二十一）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十一）

1． Folder对象
Driver对象的RootFolder属性返回一个Folder对象，通过该对象可访问这个驱动器内的所有的内容。可以使用这个Folder对象的属性和方法遍历驱动器上的目录，并得到该文件夹和其他文件夹的属性。
(1) Folder对象的属性
Folder对象提供一组属性，可用这些属性得到关于当前文件夹的更多信息，也可以改变该文件夹的名称。其属性及说明如表5-9所示：
表5-9 Folder 对象的属性及说明
属 性
说 明

Attributes
返回文件夹的属性。可以是下列值中的一个或其组合：Normal(0)、ReadOnly(1)、Hidden(2)、System(4)、Volume(名称)(8)、Directory（文件夹）(16)、Archive(32)、Alias(64)和Compressed(128)。例如，一个隐藏的只读文件，Attributes的值为3

DateCreated
返回该文件夹的创建日期和时间

DateLastAccessed
返回最后一次访问该文件夹的日期和时间

DateLastModified
返回最后一次修改该文件夹的日期和时间

Drive
返回该文件夹所在的驱动器的驱动器字母

Files
返回Folder对象包含的Files集合，表示该文件夹内所有的文件

IsRootFolder
返回一个布尔值说明该文件夹是否是当前驱动器的根文件夹

Name
设定或返回文件夹的名字

ParentFolder
返回该文件夹的父文件夹对应的Folder对象

Path
返回文件夹的绝对路径，使用相应的长文件名

ShortName
返回DOS风格的8.3形式的文件夹名

ShortPath
返回DOS风格的8.3形式的文件夹的绝对路径

Size
返回包含在该文件夹里所有文件和子文件夹的大小

SubFolers
返回该文件夹内包含的所有子文件夹对应的Folders集合，包括隐藏文件夹和系统文件夹

Type
如果可能，返回一个文件夹的说明字符串（例如，“Recycle Bin”）

(2) Folder对象的方法
Folder对象提供一组可用于复制、删除和移动当前文件夹的方法。这些方法的运行方式与FileSystemObject对象的CopyFolder、DeleFolder和MoveFolder方法相同，但这些方法不要求source参数，因为源文件就是这个文件夹。这些方法及说明如表5-10所示：
表5-10 Folder对象的方法及说明
方 法
说 明

Copy(destination,overwrite)
将这个文件夹及所有的内容复制到destination指定的文件夹。如果destination的末尾是路径分隔符(‘\’)，那么认为destination是放置拷贝文件夹的一个文件夹。否则认为destination是要创建的新文件夹的路径和名字。如果目标文件夹已经存在且overwrite参数设置为False，将产生错误，缺省的overwrite参数是True

Delete(force)
删除文件夹及里面的所有内容。如果可选的force参数设置为True，即使文件夹设置为只读或含有只读的文件，也将删除该文件夹。缺省的force是False

Move(destination)
将文件夹及里面所有的内容移动到destination指定的文件夹。如果destination的末尾是路径分隔符(‘\’)，那么认为destination是放置移动文件夹的一个文件夹。否则认为destination是一个新的文件夹的路径和名字。如果目标文件夹已经存在，则出错

CreateTextFile
(filename,overwrite,unicode)
用指定的文件名在文件夹内创建一个新的文本文件，并且返回一个相应的TextStream对象。如果可选的overwrite参数设置为True，将覆盖任何已有的同名文件。缺省的overwrite参数是False。如果可选的unicode参数设置为True，文件的内容将存储为unicode文本。缺省的unicode是False

在文件夹之间可以使用当前文件夹的ParentFolder属性，返回到父目录。当到达一个文件夹时，如果IsRootFolder属性是True，就停下来。离开驱动器的根目录，沿目录树向下，可遍历或访问在Folders集合（由当前文件夹的SubFolders属性返回）内的指定文件夹。
下列程序遍历了驱动器C根目录内的所有文件夹，并显示各个文件夹的有关信息。
VBScript程序如下：
'In VBScript:
' Create a FileSystemObject instance
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
' Get a reference to drive C
Set objDriveC = objFSO.GetDrive("C:")
' Get a reference to the root folder
Set objRoot = objDriveC.RootFolder
' Get a reference to the SubFolders collection
Set objFolders = objRoot.SubFolders
' Get a reference to the first folder in the SubFolders collection
For Each objFolder In objFolders
Set objFolder1 = objFolders.Item((objFolder.Name))
Exit For
Next
' Iterate through all the files in this folder
For Each objFile in objFolder1.Files
Response.Write "Name: " & objFile.Name & " "
Response.Write "ShortName: " & objFile.ShortName & " "
Response.Write "Size: " & objFile.Size & " bytes "
Response.Write "Type: " & objFile.Type & "<BR>"
Response.Write "Path: " & objFile.Path & " "
Response.Write "ShortPath: " & objFile.ShortPath & "<BR>"
Response.Write "Created: " & objFile.DateCreated & " "
Response.Write "LastModified: " & objFile.DateLastModified & "<P>"
Next
JScript程序如下：
//In JScript:
// Create a FileSystemObject instance
var objFSO = Server.CreateObject('Scripting.FileSystemObject');
// Get a reference to drive C
var objDriveC = objFSO.GetDrive('C:');
// Get a reference to the root folder
var objRoot = objDriveC.RootFolder;
// Get a reference to the first folder in the SubFolders collection
var colAllFolders = new Enumerator(objRoot.SubFolders);
var objFolder1 = colAllFolders.item();
// Get a reference to the Files collection for this folder
var colFiles = new Enumerator(objFolder1.Files);

// Iterate through all the files in this collection
for (; !colFiles.atEnd(); colFiles.moveNext()) {
objFile = colFiles.item()
Response.Write('Name: ' + objFile.Name + ' ');
Response.Write('ShortName: ' + objFile.ShortName + ' ');
Response.Write('Size: ' + objFile.Size + ' bytes ');
Response.Write('Type: ' + objFile.Type + '<BR>');
Response.Write('Path: ' + objFile.Path + ' ');
Response.Write('ShortPath: ' + objFile.ShortPath + '<BR>');
Response.Write('Created: ' + objFile.DateCreated + ' ');
Response.Write('Accessed: ' + objFile.DateLastAccessed + ' ');
Response.Write('Modified: ' + objFile.DateLastModified + '<P>');
}
该VBScript程序在服务器上运行时的结果如图5-12所示。该页面为folderscollection_vb.asp，来自本书提供的示例文件。

图5-12 Folders集合的内容
(3) 使用特殊文件夹
GetSpecialFolder是FileSystemObject对象的方法之一，它返回计算机上三个“特殊文件夹”对应的Folder对象：
· WindowsFolder：%Windows%目录，缺省为WinNT（或Windows，在非NT/2000计算机上）目录。
· SystemFolder：%System%目录，缺省为WinNT\System32（或Windows\System，在非NT/2000计算机上）目录。
· TemporaryFolder：%Temp%目录，缺省为WinNT\Temp（或Windows\Temp，在非NT/2000计算机上）目录。
为得到对特殊文件夹的引用，我们提供相应的预定义常数作为GetSpecialFolder方法的参数：
' In VBScript:
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")

Set objFolder = objFSO.GetSpecialFolder(WindowsFolder)
Response.Write "GetSpecialFolder(WindowsFolder) returned:<BR>"
Response.Write "Path: " & objFolder.Path & "<BR>"
Response.Write "Type: " & objFolder.Type & "<P>"

Set objFolder = objFSO.GetSpecialFolder(SystemFolder)
Response.Write "GetSpecialFolder(SystemFolder) returned:<BR>"
Response.Write "Path: " & objFolder.Path & "<BR>"
Response.Write "Type: " & objFolder.Type & "<P>"

Set objFolder = objFSO.GetSpecialFolder(TemporaryFolder)
Response.Write "GetSpecialFolder(TemporaryFolder) returned:<BR>"
Response.Write "Path: " & objFolder.Path & "<BR>"
Response.Write "Type: " & objFolder.Type & "<P>"
或用JScript：
// In JScript:
var objFSO = Server.CreateObject('Scripting.FileSystemObject');

var objFolder = objFSO.GetSpecialFolder(WindowsFolder);
Response.Write('GetSpecialFolder(WindowsFolder) returned - ');
Response.Write('Path: ' + objFolder.Path + ' ');
Response.Write('Type: ' + objFolder.Type + '<BR>');

var objFolder = objFSO.GetSpecialFolder(SystemFolder);
Response.Write('GetSpecialFolder(SystemFolder) returned - ');
Response.Write('Path: ' + objFolder.Path + ' ');
Response.Write('Type: ' + objFolder.Type + '<BR>');

var objFolder = objFSO.GetSpecialFolder(TemporaryFolder);
Response.Write('GetSpecialFolder(TemporaryFolder) returned - ');
Response.Write('Path: ' + objFolder.Path + ' ');
Response.Write('Type: ' + objFolder.Type + '<BR>');
该VBScript程序在服务器上运行时的结果如图5-13所示。该页面名为specialfolder_vb.asp，来自本书提供的示例文件。

图5-13 GetSpecialFolder方法的使用结果
2． File对象
File对象提供了对文件的属性的访问，通过它的方法能够对文件进行操作。每个Folder对象提供了一个Files集合，包含文件夹中文件对应的File对象。还可以直接地从FileSystemObject对象中通过使用GetFile方法得到一个File对象引用。
(1) File对象的属性
File对象有一系列的属性，类似于Folder对象的属性，如表5-11所示：
表5-11 File对象的属性及说明
属 性
说 明

Attributes
返回文件的属性。可以是下列值中的一个或其组合：Normal(0)、ReadOnly(1)、Hidden(2)、System(4)、Volume(名称)(9)、Directory(文件夹)(16)、Archive(32)、Alias(64)和Compressed(128)

DateCreated
返回该文件夹的创建日期和时间

DateLastAccessed
返回最后一次访问该文件的日期和时间

DateLastModified
返回最后一次修改该文件的日期和时间

Drive
返回该文件所在的驱动器的Drive对象

Name
设定或返回文件的名字

ParentFolder
返回该文件的父文件夹的Folder对象

Path
返回文件的绝对路径，可使用长文件名

ShortName
返回DOS风格的8.3形式的文件名

ShortPath
返回DOS风格的8.3形式的文件绝对路径

Size
返回该文件的大小（字节）

Type
如果可能，返回一个文件类型的说明字符串（例如：“Text Document”表示.txt文件）

(2) File对象的方法
同样类似于Folder对象，File对象的方法允许复制、删除以及移动文件。它也有一个使用文本流打开文件的方法。File对象的方法及说明如表5-12所示：
表5-12 File对象的方法及说明
方 法
说 明

Copy(destination,overwrite)
将这个文件复制到destination指定的文件夹。如果destination的末尾是路径分隔符(‘\’)，那么认为destination是放置拷贝文件的文件夹。否则认为destination是要创建的新文件的路径和名字。如果目标文件已经存在且overwrite参数设置为False，将产生错误，缺省的overwrite参数是True

Delete(force)
删除这个文件。如果可选的force参数设置为True，即使文件具有只读属性也会被删除。缺省的force是False

Move(destination)
将文件移动到destination指定的文件夹。如果destination的末尾是路径分隔符(‘\’)，那么认为destination是一文件夹。否则认为destination是一个新的文件的路径和名字。如果目标文件夹已经存在，则出错

CreateTextFile
(filename,overwrite,unicode)
用指定的文件名创建一个新的文本文件，并且返回一个相应的TextStream对象。如果可选的overwrite参数设置为True，将覆盖任何已有的同名文件。缺省的overwrite参数是False。如果可选的unicode参数设置为True，文件的内容将存储为unicode文本。缺省的unicode是False

OpenAsTextStream
(iomode,format)
打开指定文件并且返回一个TextStream对象，用于文件的读、写或追加。iomode参数指定了要求的访问类型，允许值是ForReading(1) (缺省值)、ForWrite(2)、ForAppending(8)。format参数说明了读、写文件的数据格式。允许值是TristateFalse(0)（缺省），说明用ASCII数据格式；TristateTrue(-1)说明用Unicode数据格式；TristateUseDefault(-2)说明使用系统缺省格式

因此给定一个File对象后，可以使用ParentFolder属性得到包含该文件的Folder对象的引用，用来在文件系统中导航。甚至可以用Drive属性获得相应的Drive对象的引用，并得到各种Folder对象以及所包含的File对象。
另外，给定一个Folder对象以及对应的Files集合后，可以通过遍历该集合检查这一文件夹中的每个文件。还可以使用File对象的各种方法以一定方式处理该文件，如复制、移动或删除。下面的代码给出了C驱动器的第一个文件夹的文件列表：
' In VBScript:
' Create a FileSystemObject instance
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
' Get a reference to drive C
Set objDriveC = objFSO.GetDrive("C:")
' Get a reference to the root folder
Set objRoot = objDriveC.RootFolder
' Get a reference to the SubFolders collection
Set objFolders = objRoot.SubFolders
' Get a reference to the first folder in the SubFolders collection
For Each objFolder In objFolders
Set objFolder1 = objFolders.Item((objFolder.Name))
Exit For
Next
' Iterate through all the files in this folder
For Each objFile in objFolder1.Files
Response.Write "Name: " & objFile.Name & " "
Response.Write "ShortName: " & objFile.ShortName & " "
Response.Write "Size: " & objFile.Size & " bytes "
Response.Write "Type: " & objFile.Type & "<BR>"
Response.Write "Path: " & objFile.Path & " "
Response.Write "ShortPath: " & objFile.ShortPath & "<BR>"
Response.Write "Created: " & objFile.DateCreated & " "
Response.Write "LastModified: " & objFile.DateLastModified & "<P>"
Next
注意，不能使用数字索引来定位Folders或Files集合里的条目，因此必须使用For Each … Next语句遍历该集合直到最初的条目，然后使用该条目的Name属性。也不得不使用嵌套的圆括号强迫其作为值（字符串）传送给该Folders集合的Item方法。
用下面的JScript程序可完成同样的工作：
// In JScript:
// Create a FileSystemObject instance
var objFSO = Server.CreateObject('Scripting.FileSystemObject');
// Get a reference to drive C
var objDriveC = objFSO.GetDrive('C:');
// Get a reference to the root folder
var objRoot = objDriveC.RootFolder;
// Get a reference to the first folder in the SubFolders collection
var colAllFolders = new Enumerator(objRoot.SubFolders);
var objFolder1 = colAllFolders.item();
// Get a reference to the Files collection for this folder
var colFiles = new Enumerator(objFolder1.Files);

// Iterate through all the files in this collection
for (; !colFiles.atEnd(); colFiles.moveNext()) {
objFile = colFiles.item()
Response.Write('Name: ' + objFile.Name + ' ');
Response.Write('ShortName: ' + objFile.ShortName + ' ');
Response.Write('Size: ' + objFile.Size + ' bytes ');
Response.Write('Type: ' + objFile.Type + '<BR>');
Response.Write('Path: ' + objFile.Path + ' ');
Response.Write('ShortPath: ' + objFile.ShortPath + '<BR>');
Response.Write('Created: ' + objFile.DateCreated + ' ');
Response.Write('Accessed: ' + objFile.DateLastAccessed + ' ');
Response.Write('Modified: ' + objFile.DateLastModified + '<P>');
}
两个程序的结果是相同的，如图5-14所示。该页面为filescollection_vb.asp，来自本书提供的示例文件。

图5-14 File集合的内容

5.5 Scripting.TextStream对象
FileSystemObject、Folder和File对象的一些方法都与通过TextStream对象创建、读取或写入文件有关。
虽然TextStream对象定义为FileSystemObject对象的一个独立的附属对象，但我们不得不使用FileSystemObject对象或其附属对象来创建一个TextStream对象并访问磁盘文件的内容。


ASP 3.0高级编程（二十七）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十七）

6.2.10 Tools组件
Tools组件提供了一些有用的方法，可在页面中检查文件是否存在、处理一个HTML窗体、以及产生一个随机整数，还有用于Macintosh计算机的一些方法，还可以检查是否存在某个服务器插件以及检查用户是否是网站的拥有者。
1． Tools组件的成页
Tools组件提供了五个方法，其中两个依赖于操作系统，如表6-8所示：
表6-8 Tools组件的方法及说明
方 法
说 明

FileExists(relative_url)
如果relative_url指定的文件存在，返回值为True，否则为False。必须给出虚拟相对路径及文件名，并且文件必须存在于发布的Web网站目录中。

Random()
产生一个位于-32768~32767之间的随机整数。使用ABS函数（VBScript）或Math.abs（JScript）得到在0~32768之间的正整数。使用Mod运算符（VBScript）或%运算符（JScript）得到指定范围内的一个数值。例如：
intRand = (objTools.Random Mod 76) + 25
得到一个在25~100之间的整数。

ProcessForm(output_url,
template_url,[insertion_point])
通过template_url指定的文件处理一个HTML窗体，并且插入来自窗体中已提交给当前页面的数值。结果写进output_url指定的文件，如果指定了可选项insertion_point（字符串）参数的话，组件可在已存在的输出文件中找到这个字符串，并在该位置插入新的内容。如果insertion_point参数没有指定，任何已存在的output_url文件则被新的输出取代

Owner
仅适用于Macintosh机，如果当前用户帐户是Web网站的拥有者，返回值为True，否则返回值为False

PluginExists(plugin_name)
仅适用于Macintosh机，如果指定的服务器plugin_name安装在机器上，返回值为True，否则为False

2． 使用FileExists方法
在允许用户访问之前，可以使用FileExists方法检查某些文件是否存在于服务器中（注意这个方法和FileSystemObject.FileExists以同样的方式工作）。
下面的例子中，用户提供了网页的相对URL，如果用户想通过在名为txtURL的文本框中键入URL打开网页，在重新定向之前可以检查其是否存在。
<% // in Jscript:
var objTools = Server.CreateObject('MSWC.Tools');
var strURL = Request.Form('txtURL'); // collect the page URL they entered
if (objTools.FileExists(strURL)) // see if it exists
Server.Transfer(strURL) // if it does, transfer to it
Else // or if not display a message
Response.Write('Sorry, the page you requested does not exist');
%>
这里提供了一个示例页面（使用VBScript）来演示组件的三种方法（非Macintosh），可以从ASP Installable Components主菜单中运行，如图6-16所示：

图6-16 运行Tools组件的方法的页面
网页的第一部分允许输入一个文件的相对URL，并告诉用户该文件是否存在。示例提供的缺省值是查看网站的根目录中是否有global.asa文件。点击按钮时，说明该文件是否可找到的信息将放在页面的顶部，如图6-17所示：

图6-17 运行FileExists方法的结果
把页面的所有控件放在<FORM>中，提交回本页面，这已经成为一种规范。在页面的开始，查看点击了哪个按钮。如果是FileExists的按钮，就调用组件的FileExists方法并显示合适的信息。
'look for a command sent from the FORM section buttons
If Len(Request.Form("cmdExists")) Then
strFile = Request.Form("txtFile")
If objTools.FileExists(strFile) Then
Response.Write "The file '<B>" & strFile & "</B>' does exist.<P>"
Else
Response.Write "The file '" & strFile & "' <B>does not</B> exist.<P>"
End If
End If
3．使用Tools.Random方法
在ASP页面中，有时需要一个随机数来完成某些任务，例如，把用户重新定位到一个随机网页、选择颜色或显示每日提示。可以使用VBScript中的Rnd函数，但要把所得数值转变成指定范围内的整数。Tools组件的Random方法更易于使用，因为能够直接提供整数值。
Random方法的结果是一个在-32768~32767范围中的整数值，为了获得一个指定范围的整数，可以使用脚本语言中的Abs函数并对下一个最大的整数取模。例如为了用VBScript语言创建0~20的正整数，可以使用下列语句：
intRandom = Abs(objTools.Random) Mod 21
为了得到在50~100之间的数值，可以用：
intRandom = (Abs(objTools.Random) Mod 51) + 50
示例网页使用这项技术生成随机数时，首先需要检查由用户输入的数值，以保证这些数值既是有效正整数又有正确的相对关系。
If Len(Request.Form("cmdRandom")) Then
intMin = -1 'preset to illegal values and then
intMax = -1 'only set if a valid number is entered
strMin = Request.Form("txtMinimum")
strMax = Request.Form("txtMaximum")
If IsNumeric(strMin) Then intMin = CStr(strMin)
If IsNumeric(strMax) Then intMax = CStr(strMax)
If (intMin >= 0) And (intMax > intMin) Then
intRandom = (Abs(objTools.Random) Mod (intMax - intMin + 1)) + intMin
Response.Write "Your random value is: <B>" & intRandom & "</B><P>"
Else
Response.Write "<B>The numbers you entered are not valid.</B><P>"
End If
End If
当页面重新调入时，结果显示在网页的顶部，如图6-18所示：

图6-18 运行Random方法的结果
4．使用Tools.ProcessForm方法
Tools组件中最复杂的方法是ProcessForm，用来读取存在磁盘上的临时文件，并在其中插入创建的信息（可能来自当前页面的Request.Form集合的内容），然后把结果作为一个文件输出到磁盘，这个方法的语法是：
ProcessForm (output_url, template_url, [insertion_point])
临时文件和输出文件相对于当前页面使用相对URL来定义。输出文件可以是ASP网页，如果这样，当其在浏览器打开时，将正常处理。临时文件可以包含普通的ASP代码，但不运行，仅简单地拷贝输出文件。然而，如果把临时文件中的ASP代码放在<%%…%%>限定符中，当临时文件调入时代码将被执行，这允许动态生成的数值（诸如进行处理的时间和日期）插入到输出页面中。
下面是示例文件template.asp（在chapter06目录的Tools子目录中）：
This file was created by the ASP Tools component
------------------------------------------------
The content of the request was:

Output file name: <%% = Request.Form("txtOutput") %%>
Template file name: <%% = Request.Form("txtTemplate") %%>
Insertion point text: <%% = Request.Form("txtInsert") %%>

------------------------------------------------
Created <%% = Now() %%>
示例页面包含着预定使用这个临时文件的控件，这些控件创建一个和临时文件在同一个文件夹中的名为output.asp的输出文件，如图6-19所示：

图6-19 运行Tools.ProcessForm方法的界面
点击按钮时，将运行一部分ASP代码，从文本框中采集数据并调用ProcessForm方法：
If Len(Request.Form("cmdProcess")) Then
strTemplate = Request.Form("txtTemplate")
strOutput = Request.Form("txtOutput")
strInsertPoint = Request.Form("txtInsert")
'…
' we display the template contents here
'…

' process the form contents
objTools.ProcessForm strOutput, strTemplate, strInsertPoint

'…
' we display the output file contents here
'…
End If
(1) 设置输出文件的访问权限
如果得到一个“MSWC.Tools error 80004005 Couldn’t open output file”错误信息，这意味着IIS不允许向指定的目录中写入输出文件。解决这个问题最快捷的方法是，在Properties对话框中的Security选项卡中，将相应的Tools目录以及存放output.asp的目录的Everyone组的Full Control设置成允许，如图6-20所示：

图6-20 设置输出文件访问权限的界面
(2) 查看文件内容
图6-21显示了上面代码用缺省值运行缺省值运行的结果，可以看到原来的临时文件内容，以及使用ProcessForm方法插入到输出文件中的内容。

图6-21 输出文件的内容
这里省略了显示来自前面见过的文件内容的程序代码。显示内容的方法与ASP Installable Components主菜单页面中用于显示内容链接列表文件的方法相同。使用FileSystemObject和TextStream对象的实例把整个文件读入到一个字符串中，然后将其插入网页中（记住要使用Server.HTMLEncode方法，以便把字符“<”和“>”转换成可用于显示的字符）。
QUOT = Chr(34) 'double-quote character
…
'create a FileSytemObject object to display the template file contents
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")

Response.Write "The content of the template file <B>" & _
strTemplate & "</B> is:"

Set objTStream = objFSO.OpenTextFile(Server.MapPath(strTemplate), ForReading)
strContent = Server.HTMLEncode(objTStream.ReadAll) 'read whole file
objTStream.Close

Response.Write "<DIV CLASS=" & QUOT & "showcode" & QUOT & "><PRE>" & _
strContent & "</PRE></DIV>"
…
(3) 关于插入点参数
ProcessForm方法可选的insertion_point参数能用来在文件中的特定点插入文本，这个方法是先查找输出文件中特定字符串的第一个实例，然后在该位置插入创建的新内容，在输出文件中通过把星号（*）放在insertion_point字符串前面来放置新内容。如果省略了insertion_point参数，新的内容会取代整个输出文件。
注意insertion_point参数在Tools.ProcessForm方法的早期版本中不能使用。


ASP 3.0高级编程（二十三）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十三）

第6章 活动服务器组件
在前几章中，我们已经研究了两类不同的对象：一类是ASP对象模型的内置部分，另一类是脚本运行期库提供的对象。本章将介绍第三类能用于ASP的对象，取名为活动服务器组件（或者就称为服务器组件）。
这些组件在很多场合下就如同在浏览器或在像Visual Basic编程环境中用于客户端脚本中的传统ActiveX控件一样，但是这些组件可在服务器上运行，而不是一个只能运行在客户机上的对象。
现在的问题是：这些组件来自哪里？其中一部分是由常规的ASP/IIS安装程序提供的，而另一部分可以从Web上免费或有偿获得。本章讨论ASP的各种组件的主要用途，然后介绍可得到的其他组件的几个范例。一旦感觉到使用这些活动服务器组件得心应手，你就会毫不费力将其他供应商产品嵌入到自己的ASP页面中。本章后面有一个第三方组件的列表，本书后面还将介绍如何创建自己的组件。
还有一些用于ASP的组件，但本章不准备讨论。动态Web网站技术的基本用途之一是发布直接从数据库管理系统和其他类型的数据存储获得的信息，这一用途和从数据库或其他数据存储中收集数据和存入数据的需求，促使程序员开发服务器端编程。为了使用ASP完成这些任务，我们可以充分利用称为ActiveX数据对象（ActiveX Data Objects，ADO）库的组件集。由于它涉及的内容很多，很难在本章中阐述清楚，还要考虑的其他的组件，本书从第8章开始讲述相关的内容。
本章要讨论的内容有：
· 服务器组件与在ASP中使用的其他对象的区别。
· 简要说明如何实例化对象，并得到服务器组件的引用。
· ASP 3.0和IIS提供的可安装组件，例如AD Rotator、Content Linking、Page Counter和Tools等组件。
· 一些第三方提供的组件。

6.1 脚本对象与服务器组件的比较
前一章已说明了各种脚本对象，这些对象对于ASP/IIS来说都是从脚本运行库（scrrun.dll）中得到的，重要的是不要把这些对象与服务器组件（即本章的主题）混淆一起。
服务器组件通常在其自己的DLL或可执行文件中实现，例如本章将要讨论的“Content Linking”组件，就是由nextlink.dll实现的。一旦在服务器安装和注册这个DLL文件，那么其提供的对象就可以在ASP所支持的任何脚本语言中使用。组件与系统环境的关系如图6-1所示：
图6-1 组件与系统环境的关系

使用组件服务器组件
前一章已经介绍了对象和组件在页面中通用的实例化方法，使用服务器组件和使用脚本运行期库对象的方法完全一致，唯一的区别是，不像脚本对象，服务器组件通常作为单独的DLL文件实现，而且可能要求在使用前先运行一个安装程序，或者手工注册该组件（如果这些组件在ASP缺省安装时未注册）。
1． CreateObject方法
一般说来，可以在一个ASP页面中使用ASP内置的Server对象的CreateObject方法创建一个对象，例如：
Set objThis = Server.CreateObject("this.object") ' in VBScript
或：
var objThis = Server.CreateObject('this.object'); // in JScript
这样就在变量objThis中创建了该对象的引用，然后可以在脚本中使用该对象；换句话说，可以在代码中根据需要控制其属性及调用其方法。
2． 使用<OBJECT>元素
同样可以使用常规的<OBJECT>元素创建一个对象该方法同样适用于在Web浏览器页面中创建对象。ASP支持HTML<OBJECT>元素的特殊实现，而且我们可以使用这一方法在页面中放置一个对象。要在普通的.asp文件中定义一个组件或脚本对象的实例，使用下面语句：
<OBJECT RUNAT="SERVER" SCOPE="PAGE" ID="objThis"
CLASSID="CLSID:OACE4881-8305-11CF-9427-444553540000">
</OBJECT>
我们使用SCOPE属性来设定对象的范围，选项是“SESSION”、“APPLICATION”和“PAGE”。如果在普通的.asp文件中使用<OBJECT>元素，那么必须同时使用“PAGE”属性，并忽略SCOPE属性，因为创建的对象只能在它被实例化的页面中使用。如果在global.asa文件中使用<OBJECT>元素，则所创建的对象可在当前会话或整个应用程序中使用，因此SCOPE属性可设置为“SESSION”和“APPLICATION”。
3． 检测对象实例的存在
经常出现的错误是企图创建一个未安装（或未注册）的对象的实例，或者在CreateObject方法或<OBJECT>元素中使用错误的对象ProgID。缺省状态下，ASP会出现错误并停止页面的执行。
给页面添加一些程序代码，在访问对象之前进行检测是必要的。在VBScript中，可以做到这一点，方法是：关闭缺省的错误处理，然后使用IsObject函数查看对象是否真的引用了一个对象：
' in VBScript:
…
On Error Resume Next 'turn off default error handling
Set objThis = Server.CreateObject("this.object")

If IsObject(objThis) Then
'the CreateObject method succeeded
On Error Goto 0 'turn the default error handling back on
…
'rest of the script goes here
…
Else
Response.Write "Sorry, this page cannot be accessed at present"
Response.Flush
Response.End
End If
…
在JScript语言中，可以使用JScript 5.0脚本引擎中新的错误处理特性，这在第1章中介绍过。
// in JScript:
…
try {
var objThis = Server.CreateObject('this.object');
…
// rest of the script goes here
}

catch (exception) {
Response.Write ('Sorry, this page cannot be accessed at present');
Response.Flush();
Response.End();
}
…

6.2 ASP/IIS可安装组件
ASP/IIS的标准安装包括一系列服务器组件，这些组件在IIS文档中被描述为可安装组件（installable component），尽管缺省状态下它们是安全安装。但也有可能这个名称仅仅是沿用ASP的前几个版本中的名字。在以前版本中，这些组件需要人工安装。现在，如果要给它们起个合适的名称，可称为“可实例化的组件”。
需要注意的是，在第8章将介绍一个特定的可安装组件，即ADO组件。后面的章节中使用的其他组件都是作为特定的服务或服务器应用程序的一部分而安装的。例如协作数据对象（Collaborative Date Objects，简称CDO和CDONTS）组件，用于邮件服务器和电子邮件消息。本章的其余部分将讨论，如何使用其他通用性较强的组件为Web网站或Intranet提供增强功能。
在Windows 2000中的WinNT\System32\intesrv\目录下可找到这些ASP可安装组件，该目录也放置一些组件要求的配置文件。下面按字母顺序列出了一些组件。
· Ad Rotator组件用于控制可点击图像的出现，如广告或标题。使用存储在配置文件中的信息，Ad Rotator能够依据配置文件规定的比率显示不同的图像，同时也具备把可点击的广告重新定位到相应的URL以及记录图像点击次数的特性。
· Browser Capabilities组件使用UserAgent字符串，该字符串由浏览器发往服务器，用来识别相应的浏览器。它提供了反映特定浏览器功能的一系列属性，例如是否支持脚本、cookie或Java小程序。
· Content Linking组件用于提供一系列方便导航的页面，这些页面可被改变或更新而不必编辑各个页面。该组件采用一个文本文件（含有页面URL和显示顺序），使创建按指定顺序链接起来的一些页面很容易。
· Content Rotator组件采用一种配置文件来指定插入到其他页面中的HTML、文本或程序代码，根据配置文件指定的情况，显示内容的任何部分。
· Counters组件能够创建、递增和永久地存储各个在脚本代码中使用的整数计数器的数值。
· Logging Utility组件提供对Web服务器日志文件的访问，并且允许在一个ASP页面内读取和更新这些数据。这是Windows 2000中随IIS 5.0和ASP 3.0新增的特性。
· MyInfo组件为可在整个网站内访问的名称/值对的存储提供了一个存储空间，这些数据在服务器上以XML格式的文本文件存储（不同于应用程序作用域，这些数据在Web网站范围之外仍然保留）。
· Page Counter组件记录访问一个页面的次数，可以用于跟踪页面的点击次数。
· Permission Checker组件有助于防止当一个用户试图访问没有正确的权限设置的资源时引起的脚本错误。
· Tools组件提供了一些在页面中可以采用的方法，这些方法用于检验一个文件是否存在、处理HTML窗体、生成一个随机整数。另外还有一些用在Macintosh苹果计算机上的方法，可以检查是否存在某个服务器插件程序，检查当前用户是否为网站拥有者。
另外一个由IIS/ASP提供的组件，即Status组件，只能用于Macintosh计算机的个人Web服务器上。本书没有涉及该组件，若要详细了解该组件，可通过HTTP://localhost/iis/htm/asp/complqt0.htm查阅ASP/IIS提供的IIS 5.0文档。

6.2.1 使用示例页面
本书已经提供了一系列的示例页面来说明所有这些组件的用法。
可以从Wrox Web网站上下载这些实例页面，并在自己的计算机上运行。在下载的文件的Chapter06子目录中会找到本章所用的程序代码。
在运行示例页面之前，确保MyInfo和Counter组件的“全局”实例化代码已包含在缺省Web站点的global.asa文件中。在示例文件中提供了global.asa文件中所需要的代码，可以将这些代码加到现有的global.asa文件中，不需要更换整个文件。
<!-- declare instance of the ASP Counters component with application-level scope
-->
<OBJECT ID="objCounters" RUNAT="Server" SCOPE="Application"
PROGID="MSWC.Counters">
</OBJECT>

<!-- declare instance of the ASP MyInfo component with application-level scope
-->
<OBJECT ID="objMyInfo" RUNAT="Server" SCOPE="Application" PROGID="MSWC.MyInfo">
</OBJECT>
示例文件的主菜单页面（Default.asp）使用Content Linking组件创建到每一个页面示例的链接。因此，我们将首先研究这个组件，也涉及了Ad Rotator和Counters组件的有关问题。

6.2.2 Content Linking组件
Content Linking组件对网站是非常有用的工具，它提供了内容页面，或者包含对同一站点上其他页面的链接列表的页面。它自动把当前显示页面的URL与存储在服务器里的文本文件的页面列表匹配起来并且能允许用户通过页面列表按正反顺序进行浏览。也就是说，即使访问者已经在内容页面中点击了一个链接并且正在查看列表中的某一个页面，该组件仍然会辨认出所访问的页面在列表中的位置。
因为所有详细资料均在一个文本文件中，维护网站以及页面的链接仅仅需要编辑这个文本文件。例如可以通过在内容链接列表文件中重新排列页面的顺序，来改变它们的显示顺序。
1． 内容链接列表的文件
内容链接列表文件包括一个简单的按显示顺序排列的URL列表，同时也提供了相应的描述，用于在内容页面中显示链接文本。如果需要，还可以增加对每个页面的注释，注释可以帮助识别链接，但对访问者来说注释是不可见的。
列表文件对于每个页面包含一个文本行。每一行由URL、描述和注释所组成，各部分由Tab字符（不是空格，否则文件将不能工作）分隔，最后有回车结束。例如：
newpages.htm New additions to our site we update this weekly
offers.htm Special Offers for this week we only update this monthly
register.htm Registration for new users
main.htm The main forum and chat area must be registered first
index.htm Back to the contents page
请注意目标URL必须由一个相对的虚拟或物理的路径来说明，诸如forum\enter.htm。URL不能使用“http:”、“//”或者“\\”作为开头，如果要创建上述这样含有绝对URL的菜单，可以使用ASP建立一个重新定向页面，而且在内容链接列表文件中对其进行指定，例如，建立一个名为redirect.asp的页面，包含如下代码：
<%
'redirect the request to the site specified in the query string
Response.Clear
Response.Redirect Request.QueryString("url")
%>
可以在内容链接列表文件中按下述方式使用redirect.asp文件：
redirect.asp?url=http://www.cyscape.com/ CyScape BrowserHawk Web Pages
redirect.asp?url=http://www.softartisans.com/ SA fileUp Component Pages
redirect.asp?url=http://www.stonebroom.com/swindex.htm Stonebroom RegEx Pages
2． Content Linking组件的成员
Content Linking组件提供了八种方法，如表6-1所示。这些方法能够检索来自内容链接列表文件的条目，既能相对于当前页面检索条目，也可以使用索引号检索绝对条目。第一个条目的索引号是1。
表6-1 Content Linking组件的方法及说明
方 法
说 明

GetListCount(links_file)
返回文件links_file中条目的数量

GetListIndex(links_file)
返回文件links_file中当前页面的索引号

GetNextURL(links_file)
返回文件links_file中下个页面的URL

GetNextDescription(links_file)
返回文件links_file中下个页面的描述

GetPreviousURL(links_file)
返回文件links_file中前一个页面的URL

GetPreviousDescription(links_file)
返回文件links_file中前一个页面的描述

GetNthURL(links_file,n)
返回文件links_file中第n个页面的URL

GetNthDescription(links_file,n)
返回文件links_file中第n个页面的描述

如果当前页面不在内容链接列表文件中，则：
· GetListIndex返回0。
· GetNextURL和GetNextDescription返回列表文件中最后一个页面的URL和描述。
· GetPreviousURL和GetPreviousDescription返回列表文件中第一个页面的URL和描述。
3． 使用Content Linking组件
一旦创建了自己的内容链接文件，就可以把该组件添加到页面中。这里有一个典型的示例，它遍历内容链接文件中的所有条目，并且创建一个页面列表（以超链接表示页面）。
<% ' in VBScript:
Set objNextLink = Server.CreateObject("MSWC.Nextlink")
%>

<UL>
<%
'get the number of entries in the menu file
intCount = objNextLink.GetListCount("contlink.txt")
'loop through the entries
For intLoop = 1 To intCount %>
<LI>
<A HREF="<% = objNextLink.GetNthURL("contlink.txt", intLoop) %>">
<% = objNextLink.GetNthDescription("contlink.txt", intLoop) %>
</A>
<%
Next
%>
</UL>
这段代码首先创建了一个Content Linking对象。在普通的<UL>和</UL>标记之间，使用Content Linking对象的GetListCount方法查找在内容链接列表文件有多少条目。然后再遍历这些条目，对于每一个条目在页面中放入一个<LI>标记，后面跟着一个<A>标记。HREF属性的值从列表文件中使用GetNthURL方法检索得到，用作链接文本的描述使用GetNthDescription方法得到。
注意，内容列表文件和使用它的ASP页面应处在同一文件夹中。否则，应提供一个相对物理路径或一个完整的虚拟路径，如下所示：
inCount = objNextLink.GetListCount("links\contlink.txt") 'physical path
inCount = objNextLink.GetListCount("/demo/contlink.txt") 'virtual path


ASP 3.0高级编程（二十九）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十九）

6.3.2 SA-FileUP组件
在很多Web网站上，一个常规要求是，服务器接受客户机发送的文件的能力如何？在IIS 4.0里，Microsoft提供了一个名为Posting Acceptor组件完成这项工作，IIS 5.0和ASP 3.0的标准安装中没有该组件，如果安装Site Server Express的话，则需要包含这一组件。可以从Microsoft Web网站http://www.microsoft.com/windows/software/webpost/post_accept.htm上下载该组件。
然而，很多人发现Posting Acceptor组件在功能上有局限性且很难编程。现在一个流行的替代品是来自Software Artisans的SA-FileUP组件。与Posting Acceptor组件（仅是ISAPI型过滤器）不同，SA-FileUP组件是一个真正的ActiveX DLL服务器组件，容易集成到ASP网页中。这意味着可以使用别的ASP页面或组件上使用的相同的有关设定页面安全保护及限制用户访问的技术。该组件也可在MTS/COM+的环境中上运行。
使用SA-FileUP组件的教程可在http://www.activeserverpages.com/upload/站点上找到，所以这里只给出了结果。本章的示例中包含了使用该组件的示例页面（在Chapter06目录下的SAFileUP子目录中），可用来了解如何使用该组件，有必要的话可从Software Artisans网站http://www.softartisans.com/上下载SA-FileUP的拷贝。
使用SA-FileUP组件
SA-FileUP组件的示例页面创建了一个简单的页面，包含一个FileUpload元素和一个按钮，并使用了HTML的INPUT元素，如图6-28所示：

图6-28 使用SA-FileUp组件的示例页面
<FORM ACTION="safileup_response.asp"
ENCTYPE="MULTIPART/FORM-DATA" METHOD="POST">
…
<INPUT TYPE="FILE" NAME="FILE1">
…
<INPUT TYPE="SUBMIT" NAME="SUB1" VALUE="Upload File">
…
</FORM>
用户提交窗体时，其中的内容传送给了服务器，并且ASP页面创建了一个组件实例，并且开始向服务器上传信息，以后组件返回一系列描述上传的文件的数据，如图6-29所示：

图6-29 使用SA-FileUP组件的结果
SA-FileUP组件读取文件并把它以标准的磁盘文件或二进制的数据库数据的形式暂存在服务器上。有了它，就能用ASP控制上传过程、限制上传文件的大小，甚至删除上传文件，也能动态地改变其名字和上传文件的位置。

6.3.3 RegEx注册表访问组件
Microsoft IIS 4.0提供的一个示例组件能够访问服务器上的系统注册表。这个功能有助于检查诸如安装的软件的版本，或者检查IIS和操作系统使用的各种参数。
在IIS 5.0中取消了这个组件，但如果需要这个功能，可以从其他商用版本中获得。RegEx组件是其中之一，能够访问本地机器注册表的任何部分（假如允许当前用户访问）。在ASP页面的匿名访问中，当前用户是IUSR_machinename帐户。
1． RegEx组件的成员
RegEx组件提供了四个读写注册表的方法和一个访问内部组件版本信息的方法，如表6-11所示。全部信息和文档随RegEx组件提供，其测试版本可以从http://www.stonebroom.com/下载。其附带的示例页面包含在本书提供的示例中，然而你需要自己下载组件本身。
表6-11 RegEx 组件方法及说明
方 法
说 明

GetRegValue(RegistryHive,SubKeyString)
从注册表中返回给定的子键的缺省值，如果其值不能访问的话，则返回一个空字符串

SetRegValue(RegistryHive,SubKeyString,
NewValue)
在给定的子键中，创建或更新“缺省”值，把数据类型设置成Reg_SZ（字符串）。如果更新成功的话，返回True；否则，返回False

GetRegValueEx(RegistryHive,SubKeyString,
ValueMane,ValueType)
从特定的子键中返回被命名的值，用返回数值的数据类型，更新方法的最后一个参数所指定的变量。如果数值不能访问的话，则返回一个空字符串，并且REG_ERROR作为数据类型

SetRegValueEx(RegistryHive,SubKeyString,
ValueName,NewValue,ValueType)
在给定的子键中，创建或更新被命名的数值，把数据类型设置为方法的最后一个参数指定的类型data_type。如果更新成功的话，返回True；否则返回False

GetInternalVersionNumber()
返回组件的内部版本号，如果是一个测试版本，返回值之中也包含这些信息

2． 使用RegEx组件
RegEx组件提供了一个用Visual Basic编写的示例程序，可用来试验其中包含的方法，实例程序使用RegEx组件，允许对系统注册表的任何部分以所支持的数据类型之一或以缺省的字符串数据类型读写数据，如图6-30所示。在进行任何更新之前会显示一个警告以防止意外操作。

图6-30 使用RegEx组件的示例程序
另外还有一个比较简单的ASP实例，可显示Web服务器注册表中的一些数据，并且允许读和设置一个“测试”子键。可以修改这个页面，在你自己的应用程序中使用时没有限制。图6-31所示的是在NT4和IIS 4.0上运行的结果。

图6-31 使用RegEx组件的另一示例的结果
要记住，改变注册表中的数值可能导致系统不正常地工作，编辑注册表内容这前应该备份注册表文件。

6.4 小结
本章讨论了ASP如何利用服务器组件增加其他组件所不能提供的特性，也考虑了服务器组件和其他类型的ASP对象（如ASP内置的对象和在脚本运行期库中的对象）的区别。
在研究由ASP 3.0和IIS 5.0提供的可安装组件之后，介绍了三个提供常规要求的功能的商用组件，也列出了几个可以从Web上下载的通用组件，其中有一些是免费的或者是共享/测试版本。 然而，至今我们还有一个很重要的组件没有涉及到，它就是ADO。在讨论这个问题之前，先来讨论另一个基本问题：错误处理和程序调试。


ASP 3.0高级编程（二十二）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十二）

5.5.1 创建TextStream对象的方法
有三个常用方法用于创建或打开一个文本文件并返回TextStram对象，如表5-13所示：
表5-13 创建TextStream对象的方法及说明
方 法
说 明

CreateTextFile
(filename,overwrite,unicode)
在磁盘上用指定的文件名filename创建一个新文本文件，并返回一个与该文件对应的TextStream对象。如果可选的overwrite参数设置为True，将覆盖具有同样路径的同名文件。缺省的overwrite是False。如果可选的unicode参数设置为False，该文件的内容将存储为Unicode格式。缺省的unicode是False

OpenTextFile
(filename,iomode,create,format)
打开或创建（如果不存在）一个名为filename的文件，并且返回与该文件对应的TextStream对象。filename参数可以包含绝对或相对路径。iomode参数说明需要的访问类型。容许值是ForReading(1)(缺省)、ForWriting(2)、ForAppending(8)。写入或追加到一个不存在的文件时，如果create参数设置为True，将创建一个新文件。缺省的create是False。format参数说明了读或写文件时的数据格式。容许值是TristateFalse(0)(缺省)，说明用ASCII数据格式；TristateTrue(-1)说明用Unicode数据格式；TristateUseDefault(-2)说明数据使用系统缺省的格式

OpenAsTextStream
(iomode,format)
打开一个指定的文件并且返回一个TextStream对象，可用于对该文件的读、写或追加。iomode参数说明了需要的访问类型。容许值是ForReading(1)(缺省)、ForWriting(2)、ForAppending(8)。Format参数说明了读写文件的数据格式。容许值是TristateFalse(0)(缺省)，说明用ASCII数据格式；TristateTrue(-1)说明用Unicode数据格式；TristateUseDefault(-2)说明使用系统缺省的格式

上面列出的方法在FileSystemObject、Folder和File对象中的实现有所不同。如表5-14所示：
表5-14 三个对象中包含的方法
方 法
FileSystemObject对象
Folder对象
File对象

CreateTextFile
有
有
有

OpenTextFile
有
无
无

OpenAsTextStream
无
无
有

因此，可以使用这些方法创建一个新的文本文件，或者打开一个已存在的文件。则可得到与该文件相应的一个TextStream对象，可以使用TextStream对象的属性和方法操作文件。
1． 创建新的文本文件
可以用CreateTextFile方法创建新的文本文件，或覆盖一个已存在的文件。返回的TextStream对象可用来读写文件。
首先创建一个FileSystemObject对象，用来创建TextStream对象。下面这个例子是用VBScript创建一个“普通的”（即非Unicode）名为MyFile.txt的文件，并覆盖已存在的同名文件：
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objTStream = objFSO.CreateTextFile("C:\TextFiles\MyFile.txt", True, False)
这同样可用JScript实现:
var objFSO = Server.CreateObject('Scripting.FileSystemObject');
var objTStream = objFSO.CreateTextFile('C:\TextFiles\MyFile.txt', true, false);
一旦创建了该文件，就可以使用objTStream（它是对一个TextStream对象的引用）对文件进行操作。
2． 打开已存在的文本文件
OpenTextFile方法用于打开一个已有的文本文件。它返回一个TextStream对象，可用这个对象对文件读或追加数据。
同样，首先创建一个FileSystemObject对象，然后用其创建一个TextStream对象。下面的VBScript程序例子打开一个名为MyFile.txt的文件，准备读出其内容：
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objTStream = objFSO.OpenTextFile("C:\TextFiles\MyFile.txt", ForReading)
用JScript：
var objFSO = Server.CreateObject('Scripting.FileSystemObject');
var objTStream = objFSO.OpenTextFile('C:\TextFiles\MyFile.txt', ForReading);
为了写入一个文件或创建一个不存在的文件，可以用以下代码：
' In VBScript:
Set objTStream = objFSO.OpenTextFile("C:\TextFiles\MyFile.txt", ForWriting, True)

// In JScript:
var objTStream = objFSO.OpenTextFile('C:\TextFiles\MyFile.txt', ForWriting, true);
如果要打开一个已有的Unicode文件，准备对其追加数据，但是不创建不存在的文件，可以用：
' In VBScript:
Set objTStream = objFSO.OpenTextFile("C:\TextFiles\MyFile.txt", ForReading, _
False, TristateTrue)
// In JScript:
var objTStream = objFSO.OpenTextFile('C:\TextFiles\MyFile.txt', ForReading, _
fasle, TristateTrue);
3． 作为一个TextStream对象打开一个File对象
可用File对象的OpenAsTextStream方法打开与该对象相应的文件，并且返回一个能对该文件进行读、写和追加的TextStream对象。所以，给定一个File对象（这种情况下不是FileSystemObject对象）——objFileObject，可作为一个“普通的”（非Unicode）TextStream对象打开它，以供追加文件内容：
' In VBScript:
Set objTStream = objFileObject.OpenAsTextStream(ForAppending, False)

// In JScript:
var objTStream = objFileObject.OpenTextFile(ForAppending, false);
注意，使用这种方法不需要文件名，因为程序的执行通过引用File对象进行，并且没有create参数，因为该文件必须已存在，如果想从一个新的空的文件开始，可以用：
' In VBScript:
Set objTStream = objFileObject.OpenAsTextStream(ForWriting)

// In JScript:
var objTStream = objFileObject.OpenTextFile(ForWriting);
如果想读取该文件：
' In VBScript:
Set objTStream = objFileObject.OpenAsTextStream(ForReading)

// In JScript:
var objTStream = objFileObject.OpenTextFile(ForReading);

5.5.2 TextStream对象成员概要
表5-15和表5-16是TextStream对象的全部属性和方法的列表。下面将简短介绍各个重要的成员的细节。
1． TextStream对象的属性
TextStream的属性提供有关文件内文件指针当前位置的信息，如表5-15所示。注意，所有的属性是只读的。
表5-15 TextStream对象的属性及说明
属 性
说 明

AtEndOfLine
如果文件位置指针在文件中一行的末尾则返回True

AtEndOfStream
如果文件位置指针在文件的末尾则返回True

Column
从1开始返回文件中当前字符的列号

Line
从1开始返回文件中当前行的行号

AtEndOfLine和AtEndOfStream属性仅对以iomode参数为ForReading的方式打开的文件可用，否则将会出错。
2． TextStream对象的方法
TextStream对象的方法如表5-16所示：
表5-16 TextStream对象的方法及说明
方 法
说 明

Close()
关闭一个打开的文件

Read(numchars)
从文件中读出numchars个字符

ReadAll()
作为单个字符串读出整个文件

ReadLine()
作为一个字符串从文件中读出一行（直到回车符和换行）

Skip(numchars)
当从文件读出时忽略numchars个字符

SkipLine()
当从文件读出时忽略下一行

Write(string)
向文件写入字符串string

WriteLine(string)
向文件写入字符串string（可选）和换行符

WriteBlankLines(n)
向文件写入n个换行符

3． 写文本文件
一旦使用CreateTextFile、OpenTextFile或OpenAsTextStream方法以及ForWriting或ForAppending参数，创建一个对应于某个文件的TextStream对象，可以用下面的VBScript程序写文件和关闭文件：
' In VBScript:
objTStream.WriteLine "At last I can create files with VBScript!"
objTStream.WriteLine
objTStream.WriteLine "Here are three blank lines:"
objTStream.WriteBlankLines 3
objTStream.Write "... and this is "
objTStream.WriteLine "the last line."
objTStream.Close
或者用JScript：
// In JScript:
objTStream.WriteLine('At last I can create files with JScript! ');
objTStream.WriteLine();
objTStream.WriteLine('Here are three blank lines: ');
objTStream.WriteBlankLines(3);
objTStream.Write('... and this is ');
objTStream.WriteLine('the last line. ');
objTStream.Close();
4． 读文本文件
一旦使用CreateTextFile、OpenTextFile或OpenAsTextStream方法以及ForReading参数，创建一个对应于某个文件的TextStream对象，可以用下面的VBScript程序读文件和关闭文件：
' In VBScript:
'read one line at a time until the end of the file is reached
Do While Not objTStream.AtEndOfStream
'get the line number
intLineNum = objTStream.Line
'format it as a 4-character string with leading zeros
strLineNum = Right("000" & CStr(intLineNum), 4)
'get the text of the line from the file
strLineText = objTStream.ReadLine
Response.Write strLineNum & ": " & strLineText & "<BR>"
Loop
objTStream.Close
或用JScript：
// In JScript:
// read one line at a time until the end of the file is reached
while (! objTStream.AtEndOfStream) {
// get the line number
intLineNum = objTStream.Line;
// format and convert to a string
strLineNum = '000' + intLineNum.toString();
strLineNum = substr(strLineNum, strLineNum.length – 4, 4)
// get the text of the line from the file
strLineText = objTStream.ReadLine();
Response.Write(strLineNum + ': ' + strLineText + '<BR>');
}
objTStream.Close();

5.5.3 TextStream对象举例
为了了解使用TextStream对象操作磨灭文件的几种方式，本书提供了一个VBScript示例页面，该页使用了大量的上述的代码。从示例的Chapter05主菜单页，选择链接“Working With the TextStream Object”打开show_textstream.asp页面。
该页显示了存储在磁盘上名为MyFile.txt的文件的文本内容。在<TEXTAREA>控件里显示的文本内容允许进行编辑，并且下面有三个按钮。三个按钮的作用分别是用<TEXTAREA>控件的内容更新（即取代）最初的文本，在已有文件内容的后面添加文本，或用初始的缺省内容重写文件，如图5-15所示：

图5-15 TextStream对象示例页面
1． 读取已存在的文本文件的内容
每次载入该页面，将打开文本文件并将读取的内容置入<TEXTAREA>控件。注意我们如何使用Server.MapPath方法得到文件MyFile.txt的绝对物理路径，该文件与示例页面在同一目录下。下面的程序创建FileSystemObject实例：
<%
strTextFile = Server.MapPath("MyFile.txt")

'create an instance of a FileSytemObject object
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
…
与书中本部分的大多数其他示例一样，该页包含一个<FORM>段以保存该页面的HTML控件。ACTION是当前页面，因此窗体的内容送回到同一页面。
每次载入该页面，<TEXTAREA>控件用文本文件的当前内容填充：
…
<FORM ACTION="<% = Request.ServerVariables("SCRIPT_NAME") %>" METHOD="POST">

The contents of the disk file <B><% = strTextFile %></B> are:<P>
<TEXTAREA NAME="txtContent" ROWS="10" COLS="50" WRAP="PHYSICAL">
<%
'open the text file as a TextStream object
Set objTStream = objFSO.OpenTextFile(strTextFile, ForReading)
'read one line at a time until the end of the file is reached
Do While Not objTStream.AtEndOfStream
'get the line number
intLineNum = objTStream.Line
'format and convert to a string
strLineNum = Right("00" & CStr(intLineNum), 3)
'get the text of the line from the file
strLineText = objTStream.ReadLine
Response.Write strLineNum & ": " & strLineText & vbCrLf
Loop
objTStream.Close
%>
</TEXTAREA><P>
由上面程序可知道如何打开文本文件进行读取，遍历整个文件每次读取一行（而不是作为一个字符串读出整个文件）。这是因为要添加自己的行号，行号不属于该文件的文本。对从该文件读出的每行（读之前），检索并且格式化Line属性并创建一个三位数字的行号。然后把行编号和文本行放置页面的<TEXTAREA>控件内。
2． 更新文本文件的内容
当点击页面的Update按钮时（一般是在编辑了<TEXTAREA>控件里的文本以后），将把<TEXTAREA>控件里的内容重新写入到该文本文件内。为此，该页有相应的一些ASP代码，在创建HTML控件以前检验Request.Form集合，查看点击的是哪一个按钮（如果有的话），然后就重新载入该页。
如果点击Update按钮，搜集<TEXTAREA>控件的内容作为一个字符串，分离这个字符串使之成为独立文本选择的数组，并且打开文本文件准备重写其内容，然后遍历刚刚创建的数组，按行号循环写入该行的内容：
…
'look for a command sent from the FORM section buttons
If Len(Request.Form("cmdUpdate")) Then
'Get contents of TEXTAREA control
strNewText = Request.Form("txtContent")
'Split it into an array of lines at each carriage return
arrLines = Split(strNewText, vbCrLf)
'Open the text file for writing, which replaces all existing content
Set objTStream = objFSO.OpenTextFile(strTextFile, ForWriting)
For intLine = 0 To UBound(arrLines)
strThisLine = arrLines(intLine)
'Write out each line in turn as long as it’s got a line number
If Len(strThisLine) > 4 Then objTStream.WriteLine Mid(strThisLine, 6)
Next
objTStream.Close
End If
…
HTML<TEXTAREA>控件可在返回的Value中增加额外字符，这依赖于原始HTML页内的内容格式和WRAP属性的设置。特别是应在ASP脚本结束定界符“%>”后立即写上</TEXTAREA>标记，以防止增加一个额外的回车符号。即使用：
%></TEXTAREA><p>
而不使用：
%>
</TEXTAREA><P>
3． 向文本文件追加内容
当点击Append按钮时，可对已有的文件追加内容，与修改该文件内容类似，如图5-16所示。区别是打开该文件是为了追加而不是为了改写文件。调用OpenTextFile方法时可增加额外参数，防止在指定的文件2不存在时创建新文件。
…
If Len(Request.Form("cmdAppend")) Then
'append contents of TEXTAREA to file
strNewText = Request.Form("txtContent")
arrLines = Split(strNewText, vbCrLf)
Set objTStream = objFSO.OpenTextFile(strTextFile, ForAppending, False)
For intLine = 0 To UBound(arrLines)
strThisLine = arrLines(intLine)
If Len(strThisLine) > 4 Then objTStream.WriteLine Mid(strThisLine, 6)
Next
objTStream.Close
End If
…

图5-16 向文本文件追加内容时的示例页面
4． 重写缺省内容
最后，Restore按钮用来将初始缺省内容简单地重写回文本文件。代码与用TextStream的方法写一个文本文件类似：
…
If Len(Request.Form("cmdDefault")) Then
'write out default contents to file
Set objTStream = objFSO.CreateTextFile(strTextFile, True, False)
objTStream.WriteLine "At last I can create files with VBScript!"
objTStream.WriteLine
objTStream.WriteLine "Here are three blank lines:"
objTStream.WriteBlankLines 3
objTStream.Write "... and this is "
objTStream.WriteLine "the last line."
objTStream.Close
End If

5.6 小结
本章讲述了在ASP页面中使用对象和组件的强大能力。首先讨论对象和组件的一般特性，以及它们的类型。然后集中论述了如何在ASP（及客户端）脚本代码内创建对象实例。
在页面上使用的许多对象可能都是“外部”组件，这些组件安装在服务器上，独立于ASP。本章所讨论的对象，当ASP使用一种缺省的脚本语言（如VBScript或JScript）时，总是可用的。其实现是通过scrrun.dll文件里的脚本运行期库完成的。
这些对象是指Dictonary对象、FileSystemObject对象和TextStream对象。
Dictionary对象为我们提供了存储值的一种有效方式，可根据名字进行索引和访问，而不是根据一个数字进行访问。这是存储名字/值对这样的数据的理想方式。
FileSystemObject对象和TextStream对象相互之间联系密切，可使用它们访问服务器或网络（映射）的磁盘驱动器的目录。FileSystemObject对象提供对驱动器、文件夹（目录）和文件的存取，并提供了用于对于获得更多的信息或移动、复制、删除它们的属性及方法。
可以创建对应于系统内的任何文件的TextStream对象，并通过该对象对文件进行读取和写入。对于读写过程它作为文本文件操作，甚至可以处理Unicode格式的文件。对文件系统的导航和读写能力的结合允许对服务器文件系统进行极其复杂的控制。还可以在客户端脚本代码中使用对象（有一定限制）。


ASP 3.0高级编程（二十五）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十五）

6.2.4 Counters组件
Counters组件能用于创建、存储、递增和检索每个计数器的值。不要把它和本章后面将要介绍的Page Counter组件混淆，Counters组件能用于支持任何种类数据的统计。
一个计数器含有一个整数值，能通过Counters组件的方法进行运算。使用Set方法设置计数器的指定值，用Get方法检索计数器中的值，使用Increment方法使计数器的值加1，使用Remove方法删除一个计数器。所有的计数器的值存储在一个名为Counters.txt的文本文件中，可在Counters.dll组件所在的目录中找到该文件。
1． Counters组件的成员
Counters组件提供了用于维护每个计数器组件中数值的四个方法，如表6-4所示：
表6-4 Counters组件方法及说明
方 法
说 明

Get(counter_name)
返回指定计数器的当前值，如果此计数器先前没有创建，道德创建并设置成0，其返回值为0

Increment(counter_name)
增加指定计数器的当前值，如果此计数器先前没有创建，首先创建并设置为1

Remove(counter_name)
删除指定的计数器

Ser(counter_name,value)
把指定计数器的值设置成参数value提供的整数值，如果此计数器先前没有创建，先创建并设定为指定值

2． 使用Counters组件
由于Counters.txt文件仅有一个所有组件实例都能访问的拷贝。因此，应该只创建单个的Counters组件实例，并且使之对Web网站的所有页面都是可用的，实现这一点的常用方法是在缺省Web站点根目录下的global.asa文件中创建一个应用程序范围的实例。
采用下面程序：
<!-- declare instance of the ASP Counters component with application-level scope
-->
<OBJECT ID="objCounters" RUNAT="Server" SCOPE="Application"
PROGID="MSWC.Counters">
</OBJECT>
可以使用Counters组件对需要完成的任务创建一个新的计数器。在下面的程序中，给出了有三项选择的调查问题，并对每一种选择的回答次数进行了统计，当使用者提交包含三项选择的窗体后，将调入这个页面。假设选项通过点击SUBMIT按钮的cmdYes、cmdNo和cmdMaybe来选择，其对应值分别是“是”、“否”和“可能”。
<% 'in VBScript”
If Request.Form("cmdYes") = "Yes" Then objCounter.Increment("Response_Yes")
If Request.Form("cmdNo") = "No" Then objCounter.Increment("Response_No")
If Request.Form("cmdMaybe") = "Maybe" Then
objCounter.Increment("Response_Maybe")
%>
如果这是第一次收到一个指定的响应，程序将创建一个新的计数器并自动初始化为1。
计数器在使用范围上没有限制，因为Counters对象创建在文件global.asa中，这意味着在虚拟应用程序或Web网站中创建的任何一个页面中都是可用的，所以这个“调查计数器”可用在应用程序的任何页面上，记住单个的Counters对象能提供所需的许多独立计数器，不需要创建很多Counters对象实例。
在前面的Ad Rotator组件页面示例中，研究了如何使用Counters组件存储每个广告主的点击次数，也可以在页面中使用Counters组件的Get方法显示当前值。
Wrox Press: <B><% = objCounters.Get("wrox") %></B><BR>
Stonebroom: <B><% = objCounters.Get("stonebroom") %></B><BR>
Xtras: <B><% = objCounters.Get("xtras") %></B><BR>
ComponentSource: <B><% = objCounters.Get("compsrc") %></B><BR>
Four CDs: <B><% = objCounters.Get("fourcds") %></B><BR>
Lunar: <B><% = objCounters.Get("lunar") %></B><BR>
每次加载页面时，都自动更新计数器的当前值。然而页面也包含有一些控件能调用Counters组件的其他两个方法，即删除一个计数器（相当于将其设置为0）和将计数器设置成一个指定数值，如图6-6所示：

图6-6 Counters组件的使用演示
这些控件在一个<FORM>上，点击任何一个小的空白按钮时，此窗体便提交给同一个页面，方法与本章中的所有页面所用的几乎一样。下面的程序是为Remove方法创建控件的HTML代码。
<FORM ACTION="<% = Request.ServerVariables("SCRIPT_NAME") %>" METHOD="POST">

<INPUT TYPE="SUBMIT" NAME="cmdRemove" VALUE=" ">
Counter.Remove ("
<SELECT NAME="lstRemove" SIZE="1">
<OPTION VALUE="wrox">Wrox Press</OPTION>
<OPTION VALUE="stonebroom">Stonebroom</OPTION>
<OPTION VALUE="xtras">Xtras</OPTION>
<OPTION VALUE="compsrc">ComponentSource</OPTION>
<OPTION VALUE="fourcds">Four CDs</OPTION>
<OPTION VALUE="lunar">Lunar</OPTION>
</SELECT> ")<P>
…
</FORM>
当载入页面时，通过检查Request.Form集合查看点击的按钮，如果找到了某个按钮，将运行代码的相应部分。在点击Remove按钮的情况下，相应的代码是：
If Len(Request.Form("cmdRemove")) Then
strCounterName = Request.Form("lstRemove") 'get the counter name
objCounters.Remove strCounterName
Response.Write "Removed counter '<B>" & strCounterName & "</B>'.<P>"
End If
对于Set方法，情况类似，但不仅仅需要从文本框中读取新值，而且在调用Set方法之前，检查文本框中的值是否是有效值。
If Len(Request.Form("cmdSet")) Then
strCounterName = Request.Form("lstSet") 'get the counter name
strNewValue = Request.Form("txtSet") 'get the new value
If IsNumeric(strNewValue) Then 'if it can be converted to a number
intNewValue = CInt(strNewValue) '… then convert it
objCounters.Set strCounterName, intNewValue
Response.Write "Set counter '<B>" & strCounterName & _
"</B>' to <B>" & strNewValue & "</B>.<P>"
Else
Response.Write "<B>'" & strNewValue & "</B>' is not a valid number.<P>"
End If
End If
使用页面中的按钮调用Counters对象的方法时，重新载入时会在页面顶端看到一段信息和计数器中的新值，如图6-7所示：

图6-7 调用Counters对象的方法重新载入页面

6.2.5 Browser Capabilities组件
创建各种Web网页时面临的问题之一是，不仅仅使用ASP技术创建动态网页，而且能够使用HTML元素和其他客户端技术，像Java Applets、ActiveX控件以及最近出现的HTML元素。需要意识到的是，一些访问者若使用了恰好不支持它们的浏览器，那么对于精心编制的网页，访问者看到的可能是文本、图像的杂乱组合，甚至更糟糕的还有相应工作的脚本程序代码。
这里不讨论应当如何设计支持各种不同浏览器的网页（如果想了解这方面更多的内容，可以查阅Alex Homer编写的，Wrox出版的《Professional ASP Techniques for Webmasters》一书，书号是ISN 1-861001-79-7）。然而，确实要引用某个页面时，ASP和IIS提供铁Browser Capabilities服务器组件可以用来检测浏览器所支持的相关特征。
用户请求来自服务器的页面时，浏览器传送的HTTP报头包含了正在使用的浏览器的细节。在HTTP-speak（它被称为用户代理字符串）中，定义了浏览器的名称、版本、操作系统及其兼容性。Browser Capabilities组件在自己的配置文件中查找这个字符串，并采用许多与浏览器特征等同的特性。因此，在网页运行的任何时候，Browser Capabilities组件能够提供支持或不支持某个特性的细节。
在ASP 3.0版本中，Browser Capabilities组件增加了一个新特性。在ASP页面中包含METADATA指令，指示组件从浏览器中取出一个cookie，并把其包含的任意值添加到当前的组件实例中作为新属性。这提供了一种方法，从浏览器收集更多的用户特定的信息，而不仅仅是通常从browscap.ini文件中得到的浏览器指定的信息。了解了现有的浏览器检测特性如何工作后，再回头介绍新的METADATA技术。
1． browscap.ini文件
Browser Capabilities组件使用一个基于服务器的browscap.ini文本文件，该文本文件必须和browscap.dll组件文件处于同一目录中。browscap.ini文件包含大多数关于以前和当前浏览器的信息，并且当浏览器的用户代理字符串与文件中的指定字符串都不匹配时，将使用browscap.ini文件中的缺省部分。所以添加关于浏览器的新信息或者更新现有的信息，只需编辑browscap.ini文件。
首先看一下browscap.ini文件的格式，该文件中的所有条目都是可选的。担包括缺省部分是非常重要的。如果使用的浏览器与browscap.ini文件中的任何一个都不匹配，并且没有指定缺省浏览器设置，那么所有的特性将设置成“UNKNOWN”。
下面是browscap.ini文件的格式：
; we can add comments anywhere, prefaced by a semicolon like this

; entry for a specific browser
[HTTPUserAgentHeader]
parent = browserDefinition
property1 = value1
property2 = value 2
…

[Default Browser capability Settings]
defaultProperty1 = defaultValue1
defaultProperty2 = defaultValue2
…
[HTTPUserAgentHeader]行定义了特定浏览器的起始段，并且Parent行指明了包含浏览器更多信息的另外一个定义。下面的各行定义了我们想通过Browser Capabilities组件可获得的属性以及对于该浏览器的相应值。如果浏览器没有列在所属段中，或者尽管列出了但没有列出所有的属性，将采用Default部分所列出的属性和相应的值。
例如，这个文件包含以[IE5.0]开头的段，这个段包含有Internet Explorer 5.0的相应值，这里没有parent行，显示的（除了那些在Default部分定义的）仅是我们显示定义的属性。
[IE 5.0]
browser=IE
Version=5.0
majorver=5
minorver=0
frames=TRUE
tables=TRUE
cookies=TRUE
backgroundsounds=TRUE
vbscript=TRUE
javascript=TRUE
javaapplets=TRUE
ActiveXcontrols=TRUE
Win16=False
beta=False
AK=false
SK=false
AOL=false
Update=False
此段描述不和任何一个浏览器相匹配，因为，HTTPUserAgentHeader行仅仅是[IE 5.0]。然而，如果把[IE 5.0]作为父代，可以对浏览器添加针对IE5的定义：
[Mozilla/4.0 (compatible; MSIE 5.*; Windows 95*)]
parent=IE 5.0
version=4.0
minorver=0
platform=Win95
这样我们把[IE 5.0]指定为浏览器的父代，则显式提供的属性将代替或增加给父代定义的相应的值，但这里也假定任何其他的属性值没有显式地列在其所属段中。
为了识别非常相似的浏览器版本，在HTTPUserAgentHeader行可以使用星号通配符，如：
[Mozilla/4.0 (compatible; MSIE 5.*; Windows 95*)]
将和下面的语句相匹配：
[Mozilla/4.0 (compatible; MSIE 5.0; Windows95)]
[Mozilla/4.0 (compatible; MSIE 5.5; Windows 95 AOL)]
…
然而，只有在浏览器发送的用户代理字符串和不含“*”的HTTPUserAgentHeader不完全匹配的情况下，才采用通配符匹配。也只有当这种测试失败了，字符串才会企图和含通配符的HTTPUserAgentHeader相匹配，并且使用文件中所找到的确实匹配的第一个值。
最后，加上缺省浏览器段：
[Default Browser Capability Settings]
browser=Default
Version=0.0
majorver=#0
minorver=#0
frames=False
tables=True
cookies=False
backgroundsounds=False
vbscript=False
javascript=False
javaapplets=False
ActiveXcontrols=False
…
这里假设一种最坏的情况，浏览器几乎什么都不支持。应在此基础上定义我们实际想要使用的值。但是，如果定义了一些缺省值为True，在一个UNIX终端上使用纯文本浏览器浏览页面时，可能达不到我们所希望的效果。
维护browscap.ini文件
关闭浏览器时，更新Browscap.ini文件中相应值使其与浏览器的特性保持一致，增加一些旧的或我们关注的专用的值显然也是非常重要的。为了给用户提供方便，通常可从Microsoft Web网站上下载支持ASP的一个相当全面的browscap.ini版本或其升级版本，而CrScape Inc公司提供的browscap.ini版本经常比Microsoft Web网站上的版本更新一些。
可以在http://www.cyscape.com/browscap/上找到最新的browscap.ini版本，并且订阅一份邮件列表就可自动地收到该文件的最新版本。CrScape公司也制作一个与Microsoft Browser Capabilities组件竞争的组件，称为browserHank（本章后面将介绍），新版的browscap.ini文件也可从http://www.asptracker.com上获得。
2． 使用Browser Capabilities组件
我们已经掌握了browscap.ini文件如何提供包含有关特定浏览器信息的可定制属性，下面介绍如何使用Browser Capabilities组件。相对而言，使用Browser Capabilities组件简单一些，下面创建组件的一个实例并说明其属性。
<% 'In VBScript:
Set objBCap = Server.CreateObject("MSWC.BrowserType")
blnVBScriptOK = objBCap.vbscript 'save the result in a variable
If blnVBScriptOK Then
Response.Write "This browser supports VBScript"
Else
Response.Write "This browser doesn't support VBScript"
End If
%>
上面代码程序检查浏览器是否支持VBScript并显示一个信息，可以想象这段代码根据浏览器给出的不同响应的网页，引导用户到不同的页面。
当然，使用Browser Capabilities组件的属性可做比这更复杂的工作，一个让人喜爱的技术是根据浏览器支持的属性为网站载入不同的索引网页。如果网站有一套使用帧（frame）的页面和一套不使用帧的页面，当用户第一次访问网站时，能够检查浏览器显示帧的能力，并将其重新定位到合适的索引网页上。
3． 使用Browser Capabilities的cookie特性
新版Browser Capabilities组件增加的特性之一是提供了一种方式,以获得更多的有关调用网页的特定客户的信息.
browscap.ini文件的信息只适用于特定类型的所有浏览器，所以组件仅能报告所安装的浏览器的共同特性，例如能知道浏览器是否支持cookie，但不能知道用户是否已在浏览器“选项”对话框中关闭了cookie。
同样，使用复杂的页面设计时，最好了解用户使用的连接类型，以便能选择大小适当的图像文件传送给他们，例如用户通过局域网（而不是调制解调器）连接，则允许我们提供更加丰富的环境。如果能知道用户采用的屏幕分辨率、浏览器所用的语言、操作系统和处理器类型等参数，对于我们的设计是有帮助的。
IE 5通过使用缺省行为提供这种信息，这是客户端网页的一个元素。IE 5中的行为是新增加的，其他的浏览器不支持，这是一种对网页中的元素添加特殊功能的方法，通过STYLE属性（或CSS风格表项）和元素联系起来。特别是，IE 5提供的clientCaps行为能用于提供有关客户机和浏览器设置以及当前选项的信息。
通过创建一种元素和与之相连的clientCaps行为，能通过该元素查询到有关客户的信息。下列页面来自我们提供的示例文件（browscap_cookie.htm）正是这样做的。它首先定义了应用于<IE:clientcaps>类型的所有元素的包含clientCaps行为的风格。这是XML语法，在<HTML>标记的XLMNS属生中使用为当前网页定义的名称空间。
然而，页面browscap_cookie.htm从应用clientCaps行为的元素得到一系列值，并且建立一个包含这些值的cookie，最后，把这个cookie分配给文档的cookie属性，以便有对这个特定服务器目录的页面请求时，将它传送给服务器。
<HTML XMLNS:IE>
<HEAD>
<STYLE>
IE\:clientcaps {behavior:url(#default#clientcaps)}
</STYLE>
</HEAD>
<BODY ONLOAD="createCookie();">
<IE:clientcaps ID="objCCaps" />

<SCRIPT LANGUAGE="JavaScript">

function stopAllErrors() {
return true; // prevent display of any errors
}

function createCookie() {
window.onerror = stopAllErrors;
var strCookie = new String();
strCookie = 'width=' + objCCaps.width
+ '&height=' + objCCaps.height
+ '&availWidth=' + objCCaps.availWidth
+ '&availHeight=' + objCCaps.availHeight
+ '&bufferDepth=' + objCCaps.bufferDepth
+ '&colorDepth=' + objCCaps.colorDepth
+ '&javaEnabled=' + objCCaps.javaEnabled
+ '&cookieEnabled=' + objCCaps.cookieEnabled
+ '&connectionType=' + objCCaps.connectionType
+ '&platform=' + objCCaps.platform
+ '&cpuClass=' + objCCaps.cpuClass
+ '&systemLanguage=' + objCCaps.systemLanguage
+ '&userLanguage=' + objCCaps.userLanguage;
document.cookie = 'BrowsCap=' + strCookie;
}
</SCRIPT>

</BODY>
</HTML>
为了使用这个cookie，只需把特定的METADATA指令插入到ASP页面中。如下所示：
<!-- METADATA TYPE="Cookie" NAME="BrowsCap"
SRC="browserCapabilities/browscap_cookie.htm"-->
现在，运行这个ASP网页时，会自动把页面browscap_cookie.htm发送给客户机，客户机便运行这个行为特性，然后返回cookie，随后Browser Capabilities组件把cookie的内容添加到组件实例的可用属性的列表中，查询方法与查询browscap.ini文件创建的属性所用的方法相同。
width: <B><% = objBCap.width %></B><BR>
height: <B><% = objBCap.height %></B><BR>
…
Browser Capabilities示例网页显示两类系列数值，一类是从由browscap.ini文件决定的属性中收集的数据，另一类来自客户端cookie页面。当然，不限于仅仅收集来自客户端页面中的clientCaps行为的值，使用动态HTML技术可以查询浏览器的任何属性或者是像navigator.appName这样的传统对象属性。Browser Capabilities组件示例页面如图6-8所示：
图6-8 Browser Capabilities组件示例页面


ASP 3.0高级编程（二十八）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十八）

6.2.11 Logging Utility组件
Logging Utility组件是ASP 3.0的新组件，提供了访问日志文件的方法，这个日志文件是由服务器支持的用于Web和FTP网站的IIS创建的。
1． Logging Utility组件的成员
Logging Utility组件提供了六种方法，能读取日志文件的内容、把新记录写进已存在的日志文件中。这些方法和上一章讨论的FileSystemObject对象的方法有相似性，如表6-9所示：
表6-9 Logging Utility组件的方法及说明
方 法
说 明

AtEndOfLog()
用ReadLogRecord方法遍历记录时，如果文件中的所有记录都已读出，返回值为True

CloseLogFiles(io_mode)
关闭打开的日志文件，io_mode的值指定要关闭的文件，常数ForReading(1)表示关闭打开供读出的所有文件，ForWriting(2)表示关闭打开供写入的所有文件，AllOpenFiles(32)表示关闭打开的所有文件

OpenLogFile(filename,io_mode,
service_name,service_instance,
output_format)
打开filename指定的日志文件或日志文件集，可选的io_mode参数可以设置成ForReading（缺省值，1）或ForWriting(2)。当读取取一个特定的IIS服务（例如“W3SVC”和“1”）时，可选的service_name和service_instance参数限制从文件中返回的记录。可选的output_format参数指明写入文件时使用的格式

ReadFilter(start,end)
当在规定的时间和日期范围内读取文件时，限制返回的记录，两个参数都是可选的，如果忽略的话，start是文件的第一个记录，end是最后一个记录

ReadLogRecord()
从用OpenLogFile方法打开的当前日志文件中读取下一个记录

WriteLogRecord(logging_object)
从打开供读出的文件中把记录写入一个打开供写入的文件中，参数logging_object是一个引用保存源记录的Logging Utility组件实例的对象变量

Logging Utility组件提供了二十种与IIS日志记录相对应的属性，如表6-10所示：
表6-10 Logging Utility组件的属性及说明
属 性
说 明

BytesReceived
请求时从浏览器收到的字节数

BytesSent
响应时送至浏览器的字节数

ClientIP
客户机或其主机（即代理服务器）的IP地址

Cookie
在请求中传送的cookie内容

CustumFields
加到请求中的定制报头的一个数组

DataTime
请求的日期和时间（GMT）

Method
操作类型，如“Get”和“Post”

ProtocolStatus
返回到客户机的状态消息，即“200 OK”

ProtocolVersion
协议版本字符串，如“HTTP/1.1”

Referer
包含初始化请求链接的网页的URL

ServerIP
IIS机器的IP地址

ServerName
IIS服务器的机器名

ServerPort
请求到达的端口号，如“80”

ServiceName
服务器名称，如“MSFTPSVC”或“W3SVC”

TimeTaken
检索和创建网页的总处理时间

URIQuery
请求中加到URL的查询字符串中的所有参数

URIStem
请示的目标URL

UserAgent
由客户机发送的用户代理字符串

UserName
如果不是匿名地访问服务器，UserName是用户的登录名

Win32Status
处理了请求后返回的Win32状态码

2． 使用Logging Utility组件
Logging Utility组件最普遍的应用是用于对日志文件的自定义查询。从现有文件中选择一些记录写入新文件的能力，意味着能够汇总某些类型的条目，或者有选择性地挑出一些记录将来使用。
为了使用ForReading、ForWriting和AllOpenFiles常数，必须在网页的<HEAD>部分包含METADATA指令。
<!-- METADATA TYPE="typelib" FILE="C:\WINNT\system32\inetsrv\logscrpt.dll" -->
为了遍历记录，仅须打开文件并且重复调用ReadLogRecord，直到AtEndOfLog的返回值为True。在下面的例子中将过滤记录，仅获得最后24小时以内的记录。
'Create the component instance
Set objLogUtil = Server.CreateObject("MSWC.IISLog")

'open the log file for reading, for the W3SVC instance number 1
objLogUtil.OpenLogFile "extend#.log", ForReading, "W3SVC", 1, 0

'set a filter for the last day's records only
objLogUtil.ReadFilter DateAdd("d", -1, Now), Now

'loop through the records
Do While Not objLogUtil.AtEndOfLog
objLogUtil.ReadLogRecord 'read the next record
Response.Write "Request received for page " & objLogUtil.URLStem & " on " _
& objLogUtil.DateTime & " from IP address " _
& objLogUtil.ClientIP & ".<BR>"
Loop
objLogUtil.CloseLogFiles(ForReading) 'close the file(s)
本书提供了演示使用Logging Utility组件的一个示例网页，这个网页可以从ASP Installable Components主菜单运行。
确保在Internet Services Manager中不允许匿名访问包含示例文件的目录或loggingutility.asp文件，否则将不能访问日志文件。打开loggingutility.asp文件或包含该文件的目录的Properties对话框的Directory Security选项卡，点击Anonymous access and authentication control部分的Edit按钮，打开Authentication Method对话框，并关闭Anonymous access选项。
必须改变日志文件的文件名以适合你的计算机。在缺省Web网站的Properties对话框的Web Site选项卡中，打开W3C Extended Logging，如图6-22所示：

图6-22 使用Logging Utility组件的设置1
在这个对话框中，点击Properties按钮，打开Extended Logging Properties对话框，在这里会打开要用的文件名。选择固定大小的日志文件选项，这样文件名将是extend1.log和extend2.log等等。如图6-23所示：

图6-23 使用Logging Utility组件的设置2
打开Extended Properties选项卡，确保选中想要的条目，如图6-24所示：

图6-24 使用Logging Utility组件的设置3
现在可以运行示例网页并检查结果，可以使用下列代码在HTML表中显示数值。
<%
Set objLogUtil = Server.CreateObject("MSWC.IISLog")
objLogUtil.OpenLogFile "extend#.log", ForReading, "W3SVC", 1, 0
objLogUtil.ReadFilter DateAdd("d", -1, Now), Now
%>
<TABLE CELLPADDING="10">
<TR>
<TH>Date/Time</TH>
<TH>Client IP</TH>
<TH>Bytes Sent</TH>
<TH>Target URL</TH>
</TR>
<%
Do While Not objLogUtil.AtEndOfLog
objLogUtil.ReadLogRecord 'read the next record
%>
<TR>
<TD><% = objLogUtil.DateTime %></TD>
<TD><% = objLogUtil.ClientIP %></TD>
<TD><% = objLogUtil.BytesSent %></TD>
<TD><% = objLogUtil.URLStem %></TD>
</TR>
<%
Loop
objLogUtil.CloseLogFiles(ForReading)
%>
其结果如图6-25所示：

图6-25 使用Logging Utility组件的示例运行结果

6.3 第三方服务器组件
在本节中，简要概述ASP系统中要用到的一些常见的商用和免费的第三方服务器组件。
开发Web网站时，必须完成的两个任务是管理兼容性以及向服务器上载文件。下面将介绍的两个组件能有助于完成上述的任务，而且还介绍另外一个组件，可取代Microsoft的Registry Access组件（该组件一般是从相应的Web网站得到的）。
在附录G中，给出了一些最为有用的组件的清单。

6.3.1 BrowserHawk组件
很多人使用由IIS及ASP提供的Browser Capability组件时，都会遇到一个麻烦，浏览器的定义文件（browscap.ini）要求定期的维护或替换以跟得上新版浏览器的要求。
Cyscape公司提供了broscap.ini的升级版本，同时提供了他们自己的能在服务器端和客户端进行浏览器检测的组件。而且，还提供了便捷的图形界面用于查看及修改自己的浏览器定义文件，如图6-26所示：

图6-26 BrowserHawk组件
可以看到对于每个浏览器类型，都有许多属性，如动态HTML、样式表、JavaScript版本、文件上载能力、SSL（Secure Sockets Layer）支持、操作系统细节以及语言等。与Microsoft的Browser Capability组件一样，也可以添加自己的内容。这里还有一个非常有用的具有向导风格的特性，能帮助我们把新的浏览器类型添加到定义文件中，如图6-27所示：

图6-27 向定义文件添加新浏览器的向导
BrowserHawk组件具有辩认出新的浏览器（采用一项特殊的模式匹配算法）以及那些缺省支持的一系列浏览器的能力，包括Opera和WebTV。使用过滤功能添加新属性也很容易。
当然，BrowserHawk组件的最大优点可能是定期对来自Cyscape公司的Web网站的浏览器定义文件新版本进行自动检测，同时把一些内容吸收合并到当前使用的定义文件里，这意味着没有必要不断地将定义文件拷贝到所有的Web服务器中。
BrowserHawk组件使用起来比较容易，与Browser Capability组件是兼容的，需要做的是在创建组件实例的Server.CreateObject方法或<OBJECT>元素中改变ProgID。把“MSWC.BrowserType”改为“cyScape.browserObj”。
Set objBCap = Server.CreateObject(“cyScape.browserObj”)
对可能出现的问题，应该阅读BrowserHawk组件的文档。 可从Cyscape公司的Web网站（http://www.cyscape.com）下载BrowserHawk的拷贝。本章中已经包含了使用BrowserHawk组件的示例网页，使读者能够了解如何使用BrowserHawk组件。


ASP 3.0高级编程（二十六）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十六）

6.2.6 Content Rotator组件
Content Rotator组件像一个简化的Ad Rotator组件，需要提供一种内容调度文件（Content Schedule File）。该文件仅是一种包含文本段和HTML代码段的文本文件，Content Rotator组件可在页面中自动显示其中的一个。调度表文件可以包括任何数量的文本内容条目，并可规定比率，用以控制在返回网页中每个条目的显示频率。
1． 内容调度文件
内容进度文件的结构比Ad Rotator的调度文件简单，仅需提供要使用的各个文本字符串的列表，由前面加两个百分号（%%）的语句行说明。为了设置每个条目的比率（决定它出现在返回页面中的频率），在双百分号的后面添加了一个数字，其后还可跟一对前向斜线符（//）添加注释。如：
%% 3 // This is the first entry in the schedule text file
For more information, mail us at
<A HREF=mailto:feedback@wrox.com>Wrox Press</A>

%% 4 //This is a multi-line text string
<H4>Wrox Press</H4>
<UL>
<LI> Language Primers
<LI> Advanced Programming
<LI> Internet Applications
</UL>

%% 2
Visit us on the <A HREF="http://www.wrox.com">World Wide Web</A>

这个例子说明了可以用于网页中的三个文本字符串，比率分别是3、4和2，所以条目将以3/9、4/9和2/9的频率出现。注意（和Ad Rotator组件一样）每个字符串的实际出现是随机的，然后经修正后获得相应的比率，如果调入网页九次，可能不会正好得到这个结果。
2． Content Rotator组件的成员
Content Rotator组件仅提供两个方法，用于从内容调度文件中检索内容，如表6-5所示：
表6-5 Content Rotator组件的方法及说明
方 法
说 明

GetAllContent(schdule_file)
检索和显示schdule_file文件中的所有字符串

ChooseContent(schdule_file)
检索（但不显示）来自schdule_file文件的下一个合适的内容字符串

3． 使用Content Rotator组件
为了从调度文件中获得指定段的文本和HTML，可使用对象的ChooseContent方法检索一个条目，按照内容调度文件中规定的比率。可以使用Response.Write方法将其插入到发送给客房机的输出流中。
<% 'In VBScript:
Set objMyContent = Server.CreateObject("MSWC.ContentRotator")
strContent = objMyContent.ChooseContent("ContentRotator/content_schedule.txt")
Response.Write strContent
%>
这个代码使用了和页面在同一目录中的调度文件，如果这段代码存放在其他地方，必须注明调度文件的路径，这个路径可以是一相对于Web网站根目录的物理路径或是一个完整的虚拟路径。
ObjMyContent.ChooseContent("\content\mycontent.txt") 'relative physical path
ObjMyContent.ChooseContent("/demo/mycontent.txt") 'full virtual path
如果想显示内容调度文件中的所有内容字符串，可以使用对象的GetAllContent方法，如ChooseaContent方法的情况一样，必须提供调度文件的路径。例如下面的代码得到了内容调度文件中的所有内容并显示在网页里。注意这个方法把内容自动地插入到网页中，所以不必使用Response.Write方法，这个方法也自动地在每个条目之间加了一个水平线。
ObjMyContent.GetAllContent("mycontent.txt")
为了研究Content Rotator组件的作用，从“ASP Installable Components”主菜单打开提供的示例页面，这个页面使用前面讲过的内容调度文件。调用ChooseContent方法，并把一个条目放在页面的顶部，然后使用GetAllContent方法显示调度文件中所有条目的内容。Content Rotator组件示例页面如图6-9所示：

图6-9 Content Rotator组件示例页面

6.2.7 Page Counter组件
Page Counter组件用于统计每个网页被访问的次数，该组件定期把统计的数据存入服务器磁盘上的一个文本文件——访问次数统计数据文件（Hit Count Data File）中，所以在停机或出现错误信息，当前数据也不会丢失。
1． Page Counter组件的成员
Page Counter组件提供了向该文件增加访问次数的方法以及读取和重新设置访问计数总和的方法，如表6-6所示：
方 法
说 明

Hits([[I]Page_Path[/I]])
返回由Page_Path指定的网页的访问次数，如果选项Page_Path省略的话，返回的是当前网页的访问次数

PageHit()
增加当前网页的访问次数

Reset([[I]Page_Path[/I]])
设置由Page_Path指定的网页的访问次数为0，如果选项Page_Path省略的话，设置当前网页的访问次数为0

2． 使用Page Counter组件
下面例子使用JScript语言来说明Page Counter组件的使用，先用Server.CreateObject方法创建对象实例，然后调用Hits方法得到对当前网页的访问次数。如果访问次数达到5次，则把计数器重新设置为0，并且在网页中放置一个链接，以便向访问者通过成绩。
<% // in JScript:
var objPageCount = Server.CreateObject('MSWC.PageCounter');

//look for a command sent from the FORM section buttons

// increment the counter and display the current hit count
objPageCount.PageHit();
Response.Write('You are visitor number ' + objPageCount.Hits() + '<P>');

// we treat every fifth visitor as a winner
if (objPageCount.Hits() == 5) {
objPageCount.Reset(); // reset the counter
Response.Write('You\'re a lucky winner!<BR>');
Response.Write('<A HREF="winner.asp">Go to our winners page</A><BR>');
}
%>
注意在第二个Response.Write语句中，后向斜线（\）的使用是防止JScript引擎把引号看作字符串的结束。
本书提供了一个使用Page Counter组件的示例网页。然而，由于在每个示例页面里使用一个VBScript为这个组件示例编写程序。尽管在一个ASP网页里，混用脚本语言是可能的，但在SSI的#include文件中使用脚本语言能使网页缓存出现问题，并会引起不可预测的结果。虽然脚本语言不同，但示例网页的工作方式上与上述相同。Page Counter组件示例页面如图6-10所示：

图6-10 Page Counter组件示例页面
该网页包含的<FORM>段中含有两个按钮，两个按钮都把窗体提交以重新调入网页。
<FORM ACTION="<% = Request.ServerVariables("SCRIPT_NAME") %>" METHOD="POST">
<INPUT TYPE="SUBMIT" VALUE=" "> Reload this page<P>
<INPUT TYPE="SUBMIT" NAME="cmdReset" VALUE=" ">
PageCounter.Reset( )
</FORM>
在网页的开头，与其他的示例网页一样，在请求中查找相关的按钮名字。在本页面中，我们仅对第二个名为cmdReset的按钮感兴趣，如果点击了该按钮，运行Page Counter组件的Reset方法，重新设置本页面的访问次数为0。
'look for a command sent from the FORM section buttons
If Len(Request.Form("cmdReset")) Then objPageCount.Reset 'reset the counter
前面看到的代码将调用PageHit方法把计数器设置为1，并显示在网页里。注意已经使用了Hits和Reset方法而没有给可选参数提供数值，所以它们都在当前网页计数器上操作，当然能够在另一个网页中使用，并可以对一些网页的统计结果汇总。

6.2.8 Permission Checker组件
Permission Checker组件用来检测用户当前正在访问网站的帐号是否允许读取某个特定文件或者访问某个资源（仅在Windows NT/2000而不是在Windows 9X）。可以用它来定制页面，这些网页中仅包含当前用户允许访问的资源的链接，这项技术对于隐藏限制访问的网页和资源是非常有用的，它可以使被限制的访问者不知道这些资源的存在，不会产生强行进入其中的企图。
1． Permission Checker组件的成员
Permission Checker组件只有一个方法，如表6-7所示：
表6-7 Permission Checker组件的方法及说明
方 法
说 明

HasAccess(file_path)
检查当前执行页面的用户帐号是否允许访问file_path指定的页面。该路径是一个网页、文件或资源的物理的或虚拟的路径。如果当前帐号允许访问，返回值为“True”，否则返回值为“False”

2． Permission Checker组件的工作过程
通常，除非特别规定，否则用户是匿名访问网页的，因此IIS本身会使用自己的Windows帐号访问网页，缺省值是IUSR_machinename（例如IUSR_WROXBOX），Permission Checker组件检查IUSR_machinename帐号是否能够访问指定的网页或资源。
然而，通过Internet Services Manager的Allow Anonymous Access选项关闭对Web网站或者对某一指定目录的匿名访问，可以强迫IIS提示用户输入一个有效用户帐号的用户名和口令，然后使用该帐号“非匿名访问”资源。
对一个网站或目录，在Internet Services Manager上的Properties对话框中的Directory Security选项卡中设定访问控制。在对话窗体中的Anonymous Access和Authentication Control中，点击Edit按钮打开Authentication Methods对话框，关闭Anonymous Access选项。设置界面如图6-11所示：

图6-11 访问权限的设置界面
当Permission Checker组件在用户用其自己的帐号（而不是IUSR_machinename）访问的网页中被实例化时，将检查其帐号是否可以访问这个指定的资源，如果不能使用匿名访问，那么所有的用户必须提供帐号细节。可以使用Windows Explorer对每个文件或资源设置指定的权限（在相应的Properties对话框中的Security选项卡中）。设置界面如图6-12所示：

图6-12 权限设置界面
3． 使用Permission Checker组件
可以使用Permission Checker组件检查当前用户是否使用HasAccess方法访问其他页面（网站上的任何页面）的权限。本书提供了一系列实例页面，可用来试验Permission Checker组件，打开此示例（从ASP Installable Components主菜单里打开），会看到网页里的三项链接，如图6-13所示：

图6-13 使用Permission Checker组件的示例页面
链接所指向的每一个页面都位于Chapter06示例目录下的PermissionChecker子目录中。Permission Checker组件检查当前用户是否有访问各个网页的权限。如果用户确实有访问这个网页的权限的话，这个网页的条目将是一个超级链接。
'create an instance of the component
Set objPermit = Server.CreateObject("MSWC.PermissionChecker")
%>
<UL>

<LI>
<% If objPermit.HasAccess("PermissionChecker/restricted_1.asp") Then %>
<A HREF="PermissionChecker/restricted_1.asp">
<% End If %>
Restricted Page Number 1
<% If objPermit.HasAccess("PermissionChecker/restricted_1.asp") Then %>
</A>
<% End If %>
</LI>
…
.. 'other page links here
…
</UL>
现在我们有访问三个有访问限制的网页的权限，所以这三个条目都是超级链接。然而这里不存在第四个链接（它可以被移去或是删除），因为组件不能使用当前用户（也就是IUSR_machinename）的帐号来访问它，因此不显示为超级链接。
注意这里选择仅移去超级链接但留下了链接文本，在实际情况中应删除不可访问的网页的整个条目。
如果从PermssionChecker子目录中的restricted_2.asp文件上删除Read（和任何其他的）权限，并重新调入网页，对应的条目将不显示为一个超级链接，如图6-14所示：

图6-14 删除Read权限后的页面显示

6.2.9 MyInfo组件
MyInfo组件最初是与个人Web服务器一起引入的，用于存储服务器管理员提供的个人信息。它现在可以用在ASP 3.0中，为名称/数值对，或者任何其他类型的可以表示为文本字符串的信息提供永久存储。该信息以XML格式在文本文件myinfo.xml中保存，此文件仅位于Web服务器的inetsrv目录下。在Windows 2000中，其路径为WinNT\system32\inetsrv\。
和Counter组件一样，只需创建一个简单的MyInfo组件的实例就可以使用Web网站上的所有页面均能获得其信息，确保缺省Web站点上的global.asa文件中有下面的代码：
<!-- declare instance of the ASP MyInfo component with application-level scope
-->
<OBJECT ID="objMyInfo" RUNAT="Server" SCOPE="Application" PROGID="MSWC.MyInfo">
</OBJECT>
1． 使用MyInfo组件
MyInfo组件缺省时没有属性和方法。然而可给其添加属性，简单地说，也就是赋一个名称和数值给这些属性。比如，可以添加有关工作环境和合作伙伴的信息。
<% 'in VBScript:
objMyInfo.MyManager = "Christina Chan"
objMyInfo.MyPhoneExtension = "2851"
objMyInfo.MyCarParkingSpace = "4A-17"
objMyInfo.MyComputerName = "Priscilla"
%>
同别的组件属性一样，可以使用下面代码检索数值。
<% 'in VBScript:
strManagerName = objMyInfo.MyManager
strPhoneExtension = objMyInfo.MyPhoneExtension
strParkingSpace = objMyInfo.MyCarParkingSpace
strComputerName = objMyInfo.MyComputerName
%>
这是一种在两个页面请求之间存储值的有用方法，不需要用户的Session对象。这也为将来可能改变的数值提供了一个存储区域，采用这些值的页面将会在下次运行时自动地采集这些变化的值，从而避免必须编辑大量的其他页面。
本书提供了一个使用MyInfo组件的简单示例，这个示例从ASP Installable Components主菜单中运行，使用刚才讨论的设置组件属性的代码，并可改变属性值，如图6-15所示：

图6-15 使用MyInfo组件的页面
首次打开页面时，创建属性时使用了一个标志变量SetDefaultValues，该变量存放在用户的Session对象中。当页面在每个会话里首次打开时，把属性参数设置成“缺省”值，之后，把此标志变量设置成Yes。在当前会话中，重新调入页面时，这个数值将不再重新设置缺省值。
'an instance of the component is already created in global.asa
If Not Session("SetDefaultValues") = "Yes" Then
'set the properties if they're not set to the default values,
'i.e. if this is the first time that the page has been run
'during the current user session.
objMyInfo.MyManager = "Christina Chan"
objMyInfo.MyPhoneExtension = "2851"
objMyInfo.MyCarParkingSpace = "4A-17"
objMyInfo.MyComputerName = "Priscilla"
'set a session flag to show that the default values hve been set
Session("SetDefaultValues") = "Yes"
End If
每次调用页面时，检查请求中的值是否是用于改变属性值的按钮的名称。这个按钮在一个包含下拉列表和文本框的<FORM>上。如果发现点击了这个按钮，则从下拉列表和文本框里采集数值，然后用这些数值改变相关的属性值。
'look for a command sent from the FORM section buttons
If Len(Request.Form("cmdChange")) Then
strPropertyName = Request.Form("lstName") 'get the name of the property
strNewValue = Request.Form("txtValue") 'get the new value for the property
objMyInfo(strPropertyName) = strNewValue 'set the component property value
End If


ASP 3.0高级编程（二十四）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十四）

(1) 创建一个内容页面
我们已经使用Content Linking组件为本章提供的示例创建了菜单页面。Chapter06文件夹中的页面Default.htm采用上述代码创建菜单，如图6-2所示：

图6-2 使用Content Linking组件创建的菜单页面
可以看到内容链接列表文件的内容显示在链接的下面，我们使用FileSystemObject对象完成这一工作。
<%
'create an instance of a FileSystemObject object
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")

'open the text file as a TextStream object
Set objTStream = objFSO.OpenTextFile(Server.MapPath("contlink.txt"), ForReading)
Response.Write objTStream.ReadAll 'read the whole file and put into page
ObjTStream.Close
%>
如果你编辑了contlink.txt文件，下次再次调用这个页面时，会看到菜单的条目发生了变化。注意文件中的最后三个条目，这三个条目使用前面介绍的redirect.asp技术，把绝对（而不是相对）的URL插入到列表中。
(2) 浏览这些页面
创建一个Content Linking组件实例并且使用它的一个方法时，将把当前页面的URL与指定的内容链接列表文件中的条目相匹配。不仅能用该组件创建一个内容列表（就像刚看到的那样），而且当在浏览器上打开其中一个页面时，可以用该组件对列表中的各个页面进行导航。
这意味着可以用超链接或按钮从这些页面中的一个移到另一个。例如，可以给页面添加Next和Back按钮，通过使用GetNextURL和GetPreviousURL方法能知道列表中的哪一个页面是前一个，哪一个页面是下一个。另一方面，能用GetNthURL方法跳到列表中的任何页面，用GetListIndex方法能知道当前页面在列表中的位置。
下面是一段给页面添加Next和Back按钮的代码，我们要做的只是把这段程序放在内容链接文件所列出的每一页中，或者用SSI的#include指令将它插入其中。
<!-- need a form to force Navigator to display the buttons -->
<FORM ACTION="">
<%
'we'll insert this into each page using an SSI #include statement
'create an instance of the Content Linking component
Set objNextLink = Server.CreateObject("MSWC.NextLink")
'set the content linking list file path and name
strListFile = "contlink.txt"
'get the index of the current page in the list
intThisPage = objNextLink.GetListIndex(strListFile)
If intThisPage > 1 Then 'can go back
%>
<INPUT TYPE=BUTTON VALUE="< Back"
ONCLICK="location.href='<% = objNextLink.GetPreviousURL(strListFile) %>';"
TITLE="Go to '<% = objNextLink.GetPreviousDescription(strListFile) %>'">

<%
End If
%>
<INPUT TYPE=BUTTON VALUE=" Menu " ONCLICK="location.href='Default.asp';"
TITLE="Return to the main 'Installable Components' menu">
<%
If intThisPage < objNextLink.GetListCount(strListFile) Then 'can go forward
%>

<INPUT TYPE=BUTTON VALUE="Next >"
ONCLICK="location.href='<%= objNextLink.GetNextURL(strListFile) %>';"
TITLE="Go to '<% = objNextLink.GetNextDescription(strListFile) %>'">
<%
End If
%>
</FORM>
第一步是创建Content Linking组件，然后把列表文件名存入一个变量中，以便后面在创建页面里的客户端JScript代码时能够使用这个列表文件。另外这样做也便于页面的维护，因为如果想使用一个不同文件名仅需在一个地方改变就可以了。
现在让我们了解一下在链接列表中所处的位置。GetListIndex方法提供当前页面的索引号，从列表中的第一页开始。如果当前页面的索引号比1大，那么可以后退，所以我们在页面中创建Back按钮。如果当前页面的索引号比列表中的项数少，创建Next按钮。页面中总是包括一个Home按钮，以便访问者在任何时候都能轻易地返回到内容页面。
如果打开列在主菜单中的页面之一的话，将会看到使用这项技术在每个页面上创建的一系列导航按钮，创建按钮的代码在文件contlinkbuttons.inc中，可用#include指令将它插入每个页面中。图6-3所示为带有按钮的界面。

图6-3 带有按钮的界面
在图6-3中有三个按钮，使用GetPreviousDescription方法获得上个页面的链接文件，并把这个文本放入Back按钮的TITLE属性中，作为弹出的提示。
<INPUT TYPE=BUTTON VALUE="< Back"
ONCLICK="location.href='<% = objNextLink.GetPreviousURL(strListFile) %>';"
TITLE="Go to '<% = objNextLink.GetPreviousDescription(strListFile) %>'">
Next按钮采用GetNextDescription方法完成类似的工作。
(3) 用JavaScript跳转
这些导航按钮使用JavaScript（而不是VBScript）装载新页面，这样就能够和大多数现代浏览器兼容。例如Back按钮包含下面的属性：
ONLICK="location.here='<% = objNextLink.GetPreviousURL(strListFile) %>'; "
当页面的HTML版本由ASP创建时，在<%…%>标记中的表达式用其数值取代，所以发往浏览器的网页看起来如下所示：
ONCLICK="location.herf='permissionchecker.asp'; "
为什么选择使用变量来表示内容链接列表文件的名字？其原因是在单个语句中包括一个三层的嵌套引用是非常麻烦的。
注意浏览器的缺省语言是JavaScript（或IE中的JScript），而不是VBScript，通过使用JavaScript，可以避免在OnClick代码中必须提供LANGUAGE参数，同时对非IE浏览器提供兼容性。

6.2.3 Ad Rotator组件
Ad Rotator组件允许浏览器在引用ASP页面时每次显示不同的图像，这项技术经常用在显示广告的站点上。每次打开或重新载入页面时，ASP采用“旋转调度文件”(rotator schedule file)中的信息选择一副图像，并插入网页中。然而Ad Rotator组件能做更复杂的工作，它可以把图像设置成超级链接而不是一副静止的图像，并能记录用户对每个广告的点击次数。
在本章所介绍的组件中，Ad Rotator组件可能是最难使用的，因为它涉及几个不同的文件。在查看每个文件之前，图6-4所示的工作过程总览可能有助于了解它们之间的组合方式。

图6-4 Ad Rotator组件的工作过程总览
1． 旋转调度文件
Ad Rotator组件依赖于该文件来指定显示的广告或图像。这个文件包括每个图像文件的名字、显示的尺寸大小以及显示时间的相对百分数。该文件分为两部分，由一个仅含有一个星号（*）的行分隔。
文件格式如下：
REDIRECT URL
WIDTH width
HEIGHT height
BORDER border
*
adURL
adHomeURL
text
impressions
其中：
· URL是执行重新定向的ASP文件或程序的虚拟路径及名称。
· width和height定义了页面中广告条的大小（以像素为单位），缺省值为440×60。
· border规定了广告条边界的宽度（以像素为单位），缺省值为1，无边界时为0。
第一部分是可选的，设置应用于该文件中所有广告条的缺省值。这给设置所有图像的参数提供了一种方法，边界宽度可以使用Ad Rotator组件中的border属性来设置。如果忽略了一项或多项此类任选参数，而且没有明确设置对象属性，那么参数就使用其本身和缺省值。如果忽略所有这些参数，仍需包含“*”作为文件的第一行。
第二部分，即“*”后的部分，必须存在于该文件中，它提供了各个广告条的细节。对于每个广告条，下面四个参数要重复使用。这些参数是：
· adURL，是广告图像文件的虚拟路径和文件名。
· adHomeURL，是广告跳转到的URL，连字符（-）表明该广告条没有链接。
· text，浏览器不支持图型文件时显示的文本。
· impressions，是一个整数，指明了此广告条显示的相对时间或时间比率，例如一个文件包含三个广告条，而且它们的impressions值分别设置为4、6和10，那么表明返回的页面中有20%可能出现第一个广告条，30%出现第二个广告条，50%出现第三个广告条。这没有指明广告条在浏览器中显示的实际时间。注意每一个广告条的实际出现是随机的，并被修改以达到需要的比率。
下面看一个重新定向文件的例子：
REDIRECT AdRotFiles/ad_redirect.asp
WIDTH 400
HEIGHT 50
*
Adrotfiles/wrox.gif
http://www.wrox.com/
Better Books from Wrox press
3
AdRotFiles/lunar.gif
http://www.going-to-the-moon.com/store.asp
Acme Lunar Boost Supplies
1
2． Ad Rotator组件的成员
Ad Rotator组件含有一个方法和三个属性，方法如表6-2所示：
表6-2 Ad Rotator组件的方法及说明
方 法
说 明

GetAdvertisement(schedule_file)
使用指定的schedule_file中的信息创建并返回一个字符串，这个字符串含有把下一个合适的图像插入到该页面所需的HTML

属性如表6-3所示：
属 性
说 明

Border
整数型，是指广告条边界的宽度（以像素为单位），若未设定，则采用文件第一部分设定的值

Clickable
布尔型，规定广告栏图像文件是否显示为一个超级链接，缺省值为True

TargetFram
字符串型，目标框（frame）的名字。如果用户点击图像文件的话，这个框用于显示广告主的页面。如果忽略的话，页面装载到当前浏览器的目标框或窗口内，取代含有广告栏图像的页面，这个属性也能设置成一个标准的HTML框标识：_top、_new、_child、_self、_parent和_blank

3． 使用Ad Rotator组件
下面的代码说明了Ad Rotator组件的使用。创建了组件实例之后，设置属性参数并调用GetAdvertisement方法，指定调度文件的相对物理路径。由组件返回的HTML代码通过使用Response.Write方法插入到页面中。
<% 'VBScript example
QUOT = Chr(34)
Set objAdRot = Server.CreateObject("MSWC.AdRotator")
objAdRot.Border=0 'no border
objAdRot.Clickable=TRUE 'is a hyperlink
objAdRot.TargetFrame="fraAdFrame" 'load into new window named fraAdFrame
strHTML = objAdRot.GetAdvertisement("AdRotFiles\ad_schedule.txt")
Response.Write(strHTML) 'put the HTML into the page
%>
GetAdvertisement方法的参数是相对于当前目录或Web网站根目录的调度文件物理路径和文件名。在上面的代码中，此文件取名为AdRotFiles，位于包含正在运行的ASP页面的当前目录下的子目录中。如果这个文件存入Web网站根目录下的AdRotFiles子目录，应使用“\AdRotFiles\MyAdFile.txt”。
调用GetAdvertisement方法时，返回能插入网页中用于创建广告栏的HTML代码，对于上例中的旋转调度文件中的第一个广告栏，可以得到下列代码：
<A HREF="/AdRotFiles/AdRedirect.asp?
url=http://www.wrox.com/&image=/AdRotFiles/AdPics/wrox.gif">
<IMG SRC="/AdRotFiles/AdPics/wrox.gif"
ALT="Better Books from Wrox Press" WIDTH=440 HEIGHT=60 BORDER=0></A>
可以看到在常规的<A>标记中放置了图像文件，HREF属性设置为重定向文件的名称AdRedirect.asp，同时广告主的主页URL和所用的图像添加到查询字符串中，因此当用户点击广告栏时，重定向文件将在服务器上载入并运行。
4． 重定向文件
重定向文件可以是一个ASP页面、一个ISAPI DLL或者是一个CGI应用程序，无论是哪一种，它必须能够接受和辨别广告主的主页URL以及图像名参数。它检查这些值并决定下一步该做什么。例如通常会把用户重新定向到与此广告栏有关的URL，这一点使用下面的VBScript代码很容易实现。
Response.Redirect Request.QueryString("url")
然而，重新定向文件提供给我们的不仅仅是这些。例如可以统计点击每个广告的用户数量，如果根据点击数收到广告费用的话，这将显得尤其有用。也可以用本章后面介绍的Counters组件来完成这项工作。
…
'get the URL of the advertiser's target page
strAdvertiserURL = Request.QueryString("url")
If Instr(strAdvertiserURL, "wrox.com") Then
objCounters.Increment("wrox")
Response.Clear
Response.Redirect strAdvertiserURL
End If

If Instr(strAdvertiserURL, "going-to-the-moon") Then
objCounters.Increment("lunar")
Response.Clear
Response.Write "Sorry, this isn't a real site..."
End If
…
.. 'same for other advertisers
…
查询字符串包含图像文件名时，在脚本中也要考虑这一点，如果在调度文件中，同一广告主有三种不同的图像，可以对每个图像单独地跟踪访问次数。
…
If Instr(strAdvertiserURL, "wrox.com") Then
'this is an advert for the Wrox Press site
Select Case Request.QueryString("image")
Case "books.gif"
objCounters.Increment("wrox_books")
Case "website.gif"
objCounters.Increment("wrox_site")
Case Else
objCounters.Increment("wrox_other")
End Select
Response.Clear
Response.Redirect strAdvertiserURL
End If
…
5． 一个Ad Rotator示例页面
本章提供的示例文件包括一个Ad Rotator组件演示程序，它能够从前面见过的ASP Installable Components主菜单中打开。页面的上部显示的是一个由名为ad_schedule.txt的旋转调度文件定义的广告（可在本章示例目录下的Ad RotFiles子目录中找到ad_shedule.txt），如图6-5所示：

图6-5 Ad Rotating页面的示例
使用的代码前面已经讨论过，这里在调度文件中仅增加了另外一些条目，以便更清楚地显示载入页面时的广告的变化情况。如果点击一个广告，将运行名为ad_redirect.asp的重定向文件中的代码，并且相应的广告主的页面在新的窗口里打开。完成该工作的代码和前面见到的相同。
在重定向文件中，可使用计数器（由Counters组件创建）实例存储每个广告的点击次数。
…
If Instr(strAdvertiserURL, "wrox.com") Then
objCounters.Increment("wrox")
Response.Clear
Response.Redirect strAdvertiserURL
End If
…此页面的其余部分演示了Counters组件的作用，下面介绍该组件。


ASP 3.0高级编程（二十）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二十）

5.4.1 FileSystemObject对象成员概要
FileSystemObject对象提供一个属性和一系列方法，可用它们来操纵FileSystemObject对象实现的一些从属对象。这里提供了全部的内容概要，然后介绍每一个从属对象。
1． FileSystemObject的属性
FileSystemObject对象只有一个属性，它用于得到当前机器上的所有有效驱动器的列表，如表5-4所示：
表5-4 FileSystemObject对象的属性及说明
属 性
说 明

Drivers
返回本地计算机可用的驱动器列表。

2． FileSystemObject的方法
FileSystemObject对象提供了使用从属对象的一系列方法，从属对象包括Drive、Folder和File等对象。它也实现了用于TextStream对象的两个方法：CreateTextFile和OpenTextFile。根据所使用的对象的类型，将方法划分为三类。
(1) 与驱动器有关的方法
与驱动器有关的方法如表5-5所示：
表5-5 与驱动器有关的方法及说明
方 法
说 明

DriveExists(drivespec)
如果在drivespec中指定的驱动器存在，则返回True，否则返回False。drivespec参数可以是一个驱动器字母，或者是文件、文件夹的完整绝对路径

GetDrive(drivespec)
返回drivespec指定的驱动器所对应的Drive对象。drivespec可以包含冒号、路径分隔符或者是网络共享名，即：“C”、“C:”、“C:\”及“\\machine\sharename”

GetDriveName(drivespec)
用字符串返回drivespec指定的驱动器的名称。drivespec参数必须是文件或文件夹的绝对路径，或者仅仅是驱动器字母，例如：“c:”或“c”

(2) 与文件夹有关的方法
与文件夹有关的方法如表5-6所示：
表5-6 与文件夹有关的方法及说明
方 法
说 明

BuildPath(path,name)
在已有的路径path上增添名字为name的文件或文件夹，如果需要，则增添路径分隔符’\’

CopyFolder(source,destination,overwrite)
从指定的源文件夹source（可以包含通配符）中复制一个或多个文件夹到指定的目标文件夹destination，包含了源文件夹中的所有文件。如果source包含通配符或destination末尾是路径分隔符(‘\’)，那么认为destination是要放置源文件夹的拷贝的文件夹。否则的话，认为destination是要创建的新文件夹的路径名。如果destination文件夹已经存在且overwrite参数设置为False，将产生错误，缺省的overwrite参数是True

CreateFolder(foldername)
创建一个路径名为foldername的文件夹。如果foldername已经存在将产生错误

DeleteFolder(folderspec,force)
删除由folderspec指定的一个或多个文件夹（可以在路径的最后部分包含通过配符）及文件夹中的所有内容。如果可选的force参数设置为true，那么即使文件夹包含的文件具有只读属性，也将删除该文件夹。缺省的force参数是False

FolderExist(folderspec)
如果folderspec指定的文件夹存在则返回True，否则返回False。folderspec参数可以包含文件夹的绝对或相对路径，或者仅仅是当前文件夹中看到的文件夹名

GetAbsolutePathName(pathspec)
返回明确指定文件夹的路径，其中要考虑到当前文件夹的路径。例如，如果当前文件夹是“c:\docs\sales\”，而pathspec是“jan”，返回的字符是“c:\docs\sales\jan”。通配符、”..”和”\\”路径操作符都是可以接受的

GetFolder(folderspec)
返回folderspec指定的文件夹对应的Folder对象。folderspec可以是文件夹的相对的或绝对的路径

GetParentFolderName(pathspec)
返回pathspec文件或文件夹的上一级文件夹。不检验该文件夹是否存在

GetSpecialfolder(folderspec)
返回一个特定的Windows文件夹相对应的Folder对象。参数folderspec的允许值是WindowsFolder(0)、SystemFolder(1)和TemporaryFolder(2)

MoveFolder(source,destination)
将source指定的一个或多个文件夹移动到destination指定的文件夹。在source里可以包含通配符，但在destination中不行。如果source包含通配符或destination末尾是路径分隔符(‘\’)，则认为destination是要放置源文件夹的文件夹，否则认为它是一个新文件夹的完整路径和名字。如果目的文件夹destination已经存在则产生错误

(3) 与文件有关的方法
与文件有关的方法如表5-7所示：
方 法
说 明

CopyFile(source,destination,
overwrite)
将source（可包含通配符）指定的一个或多个文件复制到指定的目标文件夹destination。如果source包含通配符或destination末尾是路径分隔符(‘\’)，那么认为destination是文件夹。否则认为destination为一新文件的完全路径和名称。如果目标文件夹已经存在且overwrite参数设置为False，将产生错误。缺省的overwrite参数是True

CreateTextFile(filename,overwrite,
unicode)
用指定的文件名filename在磁盘上创建一个新的文本文件，并返回与其对应的TextStream对象，如果可选的overwrite参数设置为True，则覆盖同一路径下已有的同名文件。缺省的overwrite参数是False。如果可选的unicode参数设置为True，则该文件的内容将存储为Unicode文本，缺省的unicode参数是False

DeleFile(filespec,force)
删除由filespec指定的一个或多个文件（可以在路径的最后部分包含通配符）。如果可选的force参数设置为true，那么也删除具有只读属性的文件。缺省的force参数是False

FileExists(filespec)
如果filespec指定的文件存在则返回True，否则返回False。filespec参数可以包含文件的绝对路径或相对路径，或者是当前文件夹中的文件名

GetBaseName(filespec)
返回filespec指定的文件的名称，即包含文件路径但去掉了文件的扩展名

GetExtensionName(filespec)
返回filespec指定的文件的扩展名

GetFile(filespec)
返回filespec指定的文件所对应的File对象。可以指定文件的相对或绝对路径

GetFileName(pathspec)
返回pathspec指定的文件的路径或文件名，如果没有文件名就返回最后的文件夹名。不检查该文件或文件夹是否存在

GetTempName()
返回一个随机产生的文件名，用于完成运算所需的临时文件或文件夹

MoveFile(source,destination)
将source指定的一个或多个源文件移动到destination指定的目的文件夹。在source里可以包含通配符，但destination不行。如果source包含通过配符或destination末尾是路径分隔符(‘\’)，那么认为destination是一文件夹。否则，认为destination是一新文件夹的完整路径和名称。如果目的文件夹已经存在则产生错误

OpenTextFile(filename,iomode,create,
format)
创建一个名叫做filename的文件，或打开一个现有的名为filename的文件，并且返回一个与其相关的TextStream对象。filename参数可以包含绝对或相对路径。iomode参数指定了所要求的访问类型。允许的数值是ForReading(1)（缺省）、ForWriting(2)、ForAppending(8)。当写入或追加到一个不存在的文件时，如果create参数设置为true，就将创建一个新文件。缺省的create参数是False。format参数说明对文件读或写的数据格式。允许数值是：TristatetFalse(0)（缺省），按照ASCII格式打开；TristatetTrue(-1)，按照Unicode格式打开；TristateDefault(-2)，用系统缺省格式打开

Unicode文件使用两个字节标识每个字符，取消了ASCII字符最多256个的限制。

5.4.2 使用驱动器
下面是使用FileSystemObject对象的简单例子，它使用DriveExists方法得到现有的驱动器字母的列表：
‘ In VBScript
Set objFSO = Server.CreateObject(“Scripting.FileSystemObject”)
For intCode = 65 To 90 ‘ANSI codes for ‘A’ to ‘Z’
strLetter = Chr(intCode)
If objFSO.DriveExists(strLetter) Then
Response.Write “Found drive “ & strLetter & “:<BR>”
End If
Next
或用JScript：
// In Jscript
var objFSO = Server.CreateObject(‘Scripting.FileSystemObject’);
for (var intCode = 65; intCode <= 90; intCode++) { //ANSI codes for ‘A’ to ‘Z’
strLetter = String.formCharCode(intCode);
If (objFSO.DriveExists(strLetter))
Response.Write (‘Found drive ‘ + strLetter + “:<BR>”);
}
这两个程序段的运行结果是相同的，如图5-9所示：

图5-9 驱动器列表
这一页面为driveexists_vb.asp，由本书的示例文件提供。
1． Drive对象
正如已经看到的，FileSystemObject对象包含一个属性——Drives，它返回一个包括本地计算机上所有可用驱动器的集合。
Drives集合里的每个条目是一个Drive对象。Drive对象的属性如表5-8所示：
表5-8 Drive对象的属性及说明
属 性
说 明

AvailableSpave
考虑了帐户定额和/或其他限制，返回驱动器上对于该用户可用的空间的大小

DriveLetter
返回驱动器的字母

DriveType
返回驱动器的类型。返回值可以是Unknown(0)、Removeable(1)、Fixed(2)、Network(3)、CDRom(4)和RamDisk(5)。然而需要注意的是当前版本的scrrun.dll不支持预定义常数Network，必须使用十进制3来代替

FileSystem
返回驱动器文件系统的类型。返回值包括“FAT”、“NTFS”和“CDFS”

FreeSpace
返回驱动器上可用剩余空间的总量

IsReady
返回一个布尔值表明驱动器是否已准备好

Path
返回一个由驱动器字母和冒号组成的驱动器路径，即“C:”

RootFolder
返回代表的驱动器根目录文件夹的Folder对象

SerialNumber
返回一个用于识别磁盘卷的十进制的序列号

ShareName
如果是一个网络驱动器，返回该驱动器的网络共享名

TotalSize
返回驱动器的总容量（以字节为单位）

VolumeName
设定或返回本地驱动器卷名

因此，通过使用Drives集合里的Drive对象，可以在服务器上产生一个驱动器列表，与通过检查每个可能的驱动器字母来判别驱动器是否存在的方法相比，效率更高。我们也可以得到关于该驱动器的信息。在VBScript里，代码如下：
' In VBScript:
' create a FileSystemObject instance
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
' create a Drives collection
Set colDrives = objFSO.Drives
' iterate through the Drives collection
For Each objDrive in colDrives

Response.Write "DriveLetter: <B>" & objDrive.DriveLetter & "</B> "
Response.Write "DriveType: <B>" & objDrive.DriveType
Select Case objDrive.DriveType
Case 0: Response.Write " - (Unknown)"
Case 1: Response.Write " - (Removable)"
Case 2: Response.Write " - (Fixed)"
Case 3: Response.Write " - (Network)"
Case 4: Response.Write " - (CDRom)"
Case 5: Response.Write " - (RamDisk)"
End Select
Response.Write "</B> "

If objDrive.DriveType = 3 Then
If objDrive.IsReady Then
Response.Write "Remote drive with ShareName: <B>" & objDrive.ShareName & "</B>"
Else
Response.Write "Remote drive - <B>IsReady</B> property returned_
<B>False</B><BR>"
End If
Else If objDrive.IsReady then
Response.Write "FileSystem: <B>" & objDrive.FileSystem & "</B> "
Response.Write "SerialNumber: <B>" & objDrive.SerialNumber & "</B><BR>"
Response.Write "Local drive with VolumeName: <B>" & _
objDrive.VolumeName & "</B><BR>"
Response.Write "AvailableSpace: <B>" & FormatNumber( _
objDrive.AvailableSpace / 1024, 0) & "</B> KB "
Response.Write "FreeSpace: <B>" & FormatNumber( _
objDrive.FreeSpace / 1024, 0) & "</B> KB "
Response.Write "TotalSize: <B>" & FormatNumber(_
objDrive.TotalSize / 1024, 0) & "</B> KB"
End if
Response.Write "<P>"
End if
Next
注意，不能用预定义常数Network比较驱动器的DriveType属性，因为（至少在scrrun.dll的当前版本中）在类型库中省略了Network常数，因此不再作为公用的常数使用。
在JScript中，该程序是：
// In JScript:
// create a FileSystemObject instance
var objFSO = Server.CreateObject('Scripting.FileSystemObject');
// create a Drives collection
var colDrives = new Enumerator(objFSO.Drives);

for (; !colDrives.atEnd(); colDrives.moveNext()) {
objDrive = colDrives.item();
Response.Write('DriveLetter: ' + objDrive.DriveLetter + '<BR>');
Response.Write('DriveType: ' + objDrive.DriveType + '<BR>');

if (objDrive.DriveType == 3)
if (objDrive.IsReady)
Response.Write('Remote drive with ShareName: ' +
objDrive.ShareName + '<BR>')
else
Response.Write('Remote drive - IsReady property returned False<BR><BR>');
else if (objDrive.IsReady) {
Response.Write('Local drive with VolumeName: ' +
objDrive.VolumeName + '<BR>');
Response.Write('FileSystem: ' + objDrive.FileSystem + '<BR>');
Response.Write('SerialNumber: ' + objDrive.SerialNumber + '<BR>');
Response.Write('AvailableSpace: ' + objDrive.AvailableSpace + ' bytes<BR>');
Response.Write('FreeSpace: ' + objDrive.FreeSpace + ' bytes<BR>');
Response.Write('TotalSize: ' + objDrive.TotalSize + ' bytes<P>');
}
}
在系统上运行这段程序以前有一点要注意。如果在A驱动器里没有磁盘，或CD-ROM驱动器里没有光盘，将得到一个错误提示：“Disk Not Ready”。除了DriveLetter属性和DriveType属性外，在使用其他属性和方法前，通过检查每个驱动器的IsReady属性，可以保护该页面。
当在服务器上运行以上VBScript代码时，运行结果如图5-10所示。这一页面为drivescollection_vb.asp，来自本书提供的示例文件。

图5-10 驱动器详细列表
2． 文件系统定位
FileSystemObject的几个方法可用于得到其他对象的引用，因此可以在服务器的文件系统和任何网络驱动器中定位。事实上，在ASP代码里使用的所有对象或组件中，除了ActiveX Data Object组件，FileSystemObject对象很可能是最复杂的对象之一。
这种复杂性是由于对如何访问文件系统的不同部分，要求有极高的灵活性。例如，可以从FileSystemObject向下通过使用各种从属对象定位一个文件。其过程是从Drives集合开始，到一个Drive对象，再到驱动器的根Folder对象，然后到子Folder对象，再到文件夹的Files集合，最后到集合内的File对象。
另外，如果已知要访问的驱动器、文件夹或文件。可以直接对其使用GetDrive、GetFolder、GetSpecialFolder和GetFile方法。图5-11有助于理解所有文件系统定位相关的组件、对象、方法和属性之间的关系。


ASP 3.0高级编程（二）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（二）

使用Form和QueryString集合
当用户填写页面<FORM>内容时所提供的全部值，或在浏览器地址栏输入在URL后的值，通过Form和QueryString集合为ASP脚本所用。这是在ASP代码中访问值的一种简单方法。
1、 访问ASP集合的一般技术
大多数ASP集合与在VB中见到的普通集合相差不多。实际上，它们是值的数组，但能通过使用一个文本字符串键（对大小不敏感）以及一个整型索引进行访问。因此，假如客户端Web页面包含的<FORM>如下：
<FORM ACTION=”show_request.asp” METHOD=”POST”>
FirstName:<INPUT TYPE=”TEXT” NAME=”FirstName”>
LastName:<INPUT TYPE=”TEXT” NAME=”LastName”>
<INPUT TYPE=”SUBMIT” VALUE=”Send”>
</FORM>
可通过访问ASP的Form集合来访问其控件内的值：
strFirstName = Request.Form(“FirstName”)
strLastName = Request.Form(“LastName”)
也可使用窗体中控件的整型索引，索引的范围从在HTML中第一个定义的控件开始，然后根据定义的顺序排序：
strFirstName = Request.Form(1)
strLastName = Request.Form(2)
然而，后面的这种以整型为索引的技术不推荐使用，因为一旦有HTML中的控件发生了变化，或者插入一个新的控件，则ASP代码将得到错误的值。进一步而言，对于阅读代码的人来讲，极容易混淆。
1) 访问集合的全部值
可以通过引用集合把整个Form上的一系列值变成单个的字符变量，且不用提供键或索引。
StrAllFormContent = Request.Form
假如文本框包含值Priscilla和Descartes，则Request.Form语句将返回下列字符：
FirstName=Priscilla&LastName=Descartes
注意，提供的值是以名称/值对的形式出现的（即控件名称=控件值），并且每一对名称/值相互之间是用符号“&”相分隔的。假如打算把窗体中的内容传递单独的，希望得到值的标准格式的可执行应用程序或DLL，这个技术是很有用的。然而，一般说来，都是通过以窗体中控件的名称为文本键来访问集合中的内容。
2) 遍历一个ASP集合
有两种方式遍历一个ASP集合中的所有成员，方式与普通VB集合的基本相同。每个集合提供一个Count属性，返回的是集合中条目数量。可通过使用一个整型索引使用Count属性来遍历。
For intLoop=1 To Request.Form.Count
Response.Write Request.Form(intLoop) & “<BR>”
Next
假如先前的窗体包含Priscilla和Descartes值的两个文本框，将得到如下结果：
Priscilla
Descartes
然而，更好的方法是使用For Each...Next结构。
For Each objItem In Request.Form
Response.Write objItem & “=” & Request.Form(objItem) & “<BR>”
Next
这带来的好处是既可以访问控件的名称又可访问其值。上述代码将得到如下结果：
FirstName = Priscilla
LastName = Descartes
注意，一些浏览器返回到ASP的<FORM>值可能与页面上显示的顺序不尽相同。
3) 集合成员的多值性
在某些情况下，ASP集合中的各个成员可能不止一个值，这种情况发生在HTML定义中有几个控件有相同Name属性时。例如：
<FORM ACTION=”Show_request.asp” METHOD=”POST”>
<INPUT TYPE=”TEXT” NAME=”OtherHobby”>
<INPUT TYPE=”TEXT” NAME=”OtherHobby”>
<INPUT TYPE=”TEXT” NAME=”OtherHobby”>
<INPUT TYPE=”SUBMIT” VALUE=”Send”>
</FORM>
在Form集合中，将为“OtherHobby”创建一个条目。然而，它将包括从三个文本框中得到的值。假如在提交时，用户留下了一个或多个为空，则返回的值为空字符串。假如用户在第一和第三个文本框分别输入Gardening和Mountaineering，第二个文本框为空，在我们的ASP代码中访问Request.Form(“OtherHobby”)，将返回字符串：
Gardening, ,Mountaineering
为了能够在这种情况下，访问单个值，可以用复杂一些的代码：
For Each objItem In Request.Form
If Request.Form(objItem).Count >1 Then ‘More than one value in this item
Response.Write objItem & “:<BR>”
For intLoop = 1 To Request.Form(objItem).Count
Response.Write “Subkey” & intLoop & “value = “_
& Request.Form(objItem) (intLoop) & “<BR>”
Next
Else
Response.Write objItem & “ = ” & Request.Form(objItem) & “<BR>”
End If
Next
对于前面的包含三个OtherHobby控件的窗体实例，这将返回：
OtherHobby:
Subkey 1 value = Gardening
Subkey 2 value =
Subkey 3 value = Mountaineering
然而，由于很少给多个文本框相同的名字，因此这种技术很少用到。
a) HTML中的单选或选页按钮控件
在HTML中，需要给几个控件相同的Name属性的情况是单选（或选项）按钮，例如：
<FORM ACTION=”show_request.asp” METHOD=”POST”>
I live in:
<INPUT TYPE=”RADIO” NAME=”Country” VALUE=”AM”>America<BR>
<INPUT TYPE=”RADIO” NAME=”Country” VALUE=”EU”>Europe<BR>
<INPUT TYPE=”RADIO” NAME=”Country” VALUE=”AS”>Asia<BR>
<INPUT TYPE=”SUBMIT” VALUE=”Send”>
</FORM>
因为用户只能选择多项中的一个（这就是给它们相同的名字的原因），将仅得到一个返回值，浏览器只能发送所选择控件的值。因此，假如这个窗体的用户已经选择了“Europez”，将得到这个条目，通过遍历Form集得到其值：
Country = EU
由于为每个控件提供了不同的VALUE属性，反映了每个条目所对应的国家或地区的名称。假如省略了VALUE属性，浏览器将返回的值为“on”，因此将得到：
Country = on
这是不经常用到的，因此一般对使用相同名称的单选控件使用VALUE属性。
b) HTML复选框控件
当一个窗体中HTML源码包含一个复选框控件时，一般都给定唯一的名称，例如：
<FORM ACTION=”show_request.asp” METHOD=”POST”>
I enjoy:
<INPUT TYPE=”CHECKBOX” NAME=”Reading” CHECKED> Reading
<INPUT TYPE=”CHECKBOX” NAME=”Eating”> Eating
<INPUT TYPE=”CHECKBOX” NAME=”Sleeping”> Sleeping
<INPUT TYPE=”SUBMIT” VALUE=”Send”>
</FORM>
在这种情况下，提交窗体时，假如仅是第一和第三个复选框被选中（加标记），遍历Form集合时，会得到下列值：
Reading = on
Sleeping = on
然而，假如为每个复选框提供一个值，把这个值发往服务器代替字符串“on”。例如窗体如下：
<FORM ACTION=”show_request.asp” METHOD=”POST”>
I enjoy:
<INPUT TYPE=”CHECKBOX” VALUE=”Hobby025” NAME=”Hobby” CHECKED>_
Swimming
<INPUT TYPE=”CHECKBOX” VALUE=”Hobby003” NAME=”Hobby” CHECKED>_
Reading
<INPUT TYPE=”CHECKBOX” VALUE=”Hobby068” NAME=”Hobby”>Eating
<INPUT TYPE=”CHECKBOX” VALUE=”Hobby010” NAME=”Hobby”>Sleeping
<INPUT TYPE=”SUBMIT” VALUE=”Send”>
</FORM>
如果除第三个复选框外，全部提交，在Request.Form集合会产生下列结果：
Hobby = Hobby025, Hobby003, Hobby010
假如编写更复杂一些集合遍历代码，如先前所述（单独显示每个子键），就得到这样结果：
Hobby:
Subkey 1 value = Hobby025
Subkey 2 value = Hobby003
Subkey 3 value = Hobby010
需要注意的是两种情况，没有选中的控件根本不返回任何值。在第一种情况的结果里，没有欺骗性的逗号，第二种情况也没有空值。这与上述的使用文本框的相当的测试的结果不一样。使用文本框时，每个文本框都返回一个值，即使是一个空字符串。这是浏览器造成这样的结果。因此在ASP代码中访问集合时，要注意这个问题。
上述情况一个棘手的负作用是使用复选框时，复选框值的索引与在原始的HTML中控件的位置没有任何联系，在上述的例子中第四个复选框的子键数为3，因为当窗体提交时，第二个控件没有选中。
c) HTML列表控件
HTML中的<SELECT>标记用来产生标准的下拉列表框，其值以一种有趣的混合方式表示。下列的窗体创建了包含5个值可供用户选择，由于包含了MULTIPLE属性，因此可以通过选择时按下Shift或Ctrl键，选择不仅一个的条目。
<FORM ACTION=”show_request.asp” METHOD=”POST”>
<SELECT NAME=”Hobby” SIZE=”5” MULTIPLE>
<OPTION VALUE=”Hobby001”>Programming</OPTION>
<OPTION VALUE=”Hobby025”>Swimming</OPTION>
<OPTION VALUE=”Hobby003”>Reading</OPTION>
<OPTION VALUE=”Hobby068”>Eating</OPTION>
<OPTION VALUE=”Hobby010”>Sleeping</OPTION>
</SELECT><P>
<INPUT TYPE=”SUBMIT” VALUE=”Send”>
</FORM>
下图所示为该页面，显示的是选中了三个条目。

这种特殊的情况返回的是在Form集合中单个条目，它包含选择的值（单个的<OPTION>标记中指定的VALUE属性），用逗号分隔：
Hobby = Hobby025, Hobby003, Hobby010
假如使用更加复杂一些的集合遍历代码（单独显示每个子键），将得到：
Hobby:
Subkey 1 value = Hobby025
Subkey 2 value = Hobby003
Subkey 3 value = Hobby010
这与上述相同名称的复选框的情况相同。事实上可以认为一个SELECT列表是一列复选框的列表供选择（不是选中）相应的条目。
然而，列表框也有指定的值，假如在<OPTION>标记中设置VALUE属性，将得到的是选择的选项的文本内容，Request.Form集将包含这样一个项目：
Hobby = Swimming, Reading, Sleeping
并且，同样，复杂一些的集合遍历代码将返回如下结果：
Hobby:
Subkey 1 value = Swimming
Subkey 2 value = Reading
Subkey 3 value = Sleeping
当然，假如单个项目被选择，且在<OPTION>中提供了VALUE属性，得到结果包含的仅是：
Hobby = Hobby025
如果没有提供VALUE属性，得到：
Hobby = Swimming
这允许既可以缺省（即无VALUE）显示选项文本，也可做相应的改变。后一种情况在某些情况下是极为有用的，如要显示（一个说明的字符串）和传递一个完全不同的内容（如用一个短码代表一个说明性的字符串）。
d) HTML提交和图像控件
复选框和单选框是布尔型控件的例子，选中或选择返回的为“on”，不像文本框和大多数其他的HTML控件，浏览器不包含没有选中或没有选择的控件的值。
还有另外一种常用的布尔型控件，称为HTML按钮。如<INPUT TYPE=”SUBMIT”>、<INPUT TYPE=”RESET”>、<INPUT TYPE=”IMAGE”>、<INPUT TYPE=”BUTTON”>和<BUTTON>...</BUTTON>类型。
BUTTON类型的控件不返回任何值，因其对窗体没有直接的影响。即使使用用来调用窗体的Submit方法，浏览器在任何请求中将不包含BUTTON类型控件的值。同样，一个<INPUT TYPE=”RESET”>按钮的值也决不会发往服务器。
然而，输入按钮控件SUBMIT和IMAGE类型实际提交窗体给服务器，其VALUE属性包含窗体的其他控件的值（只要在HTML定义中包含一个NAME属性）。例如，这个窗体可能是向导类型Web应用程序的一部分，允许用户一步步进行或取消进程：
<FORM ACTION=”show_request.asp” METHOD=”POST”>
<INPUT TYPE=”SUBMIT” NAME=”btnSubmit” VALUE=”Next”>
<INPUT TYPE=”SUBMIT” NAME=”btnSubmit” VALUE=”Previous”>
<INPUT TYPE=”SUBIMT” NAME=”btnSubmit” VALUE=”Cancel”>
</FORM>
在一个窗体中，可以包括多个SUBMIT按钮。在这种情况下，应该给每一个按钮唯一的VALUE属性，如上所示。当一个窗体被提交时，遍历Request.Form集合的值，将产生一个值，这个值依赖于按下哪个按钮用于提交这个窗体。假如用户按下的“Previous”按钮，将得到：
btnSubmit = Previous
因此，可查询Request.Form集合来决定下一个显示的页面，例如：
Select Case Request.Form(“btnSubmit”)
Case “Next”
Response.Redirect “page_3.asp”
Case “Previous”
Response.Redirect “page_1.asp”
Case “Cancel”
Response.Redirect “main_menu.asp”
End Select
同时，也可根据需要对每个按钮使用不同的NAME属性。且选择其值包含在Form集合中的控件名称。在控件没有一个完整的标记而是随后跟着较长的文本标签的情况下，极为有用，如下图所示。

此屏幕上的界面由下列代码产生：
<FORM ACTION=”show_request.asp” METHOD=”POST”>
<B>What do you want to do now?</B><P>
<INPUT TYPE=”SUBMIT” NAME=”btnNext” VALUE= ”> Go on the next page<P>
<INPUT TYPE=”SUBMIT” NAME=”btnPrevious” Value=” ”> GO back to the previous page<P>
<INPUT TYPE=”SUBMIT” NAME=”btnCancel” VALUE=” ”> Cancel and go back to the main menu page<P>
</FORM>
在ASP页面中，接收到数据后，可以检查按扭名称提供的值来判断按下的是哪个按钮。
If Len(Request.Form(“btnNext”)) Then Response.Redirect “page_3.asp”
If Len(Request.Form(“btnPrevious”)) Then Response.Redirect “page_1.asp”
If Len(Request.Form(“btnCancel”)) Then Response.Redirect “main_menu.asp”
这个工作是查询一个键上的ASP集合，如果不存在则返回一个空的字符串。换句话说，如果第二个按钮（previous页）按下，则Request.Form(“btnNext”)的值是一个空字符串，则其长度为零而不至于产生一个错误。当第二个按钮按下时，则在Form集合中这个条目的值Request.Form（“btnPrevious”），将是“ ”其长度大于零。
e) 提高使用Request集合的效率
访问一个ASP集合来下载一个值是费时的需计算资源的过程，因为这个操作包含了一系列对相关集合的搜索，这比访问一个局部变量要慢得多。因此，如果打算在页面中多次使用集合中的一个值，应该考虑将其存贮成为一个局部变量，例如：
strTitle = Request.Form(“Title”)
strFirstName = Request.Form(“FirstName”)
strLastName = Request.Form(“LastName”)
If Len(stTitle) Then strTitle = strTitle & “ “
If strFirstName = “ “ Then
StrFullName = strTitle & “ “ & strLastName
ElseIf Len(strFirstName) = 1 Then
StrFullName = strTitle & strFirstName & “· “ & strLastName
Else
StrFullName = strTitle & strFirstName & “ ” & strLastName
End If
f) 搜索所有的Request集合
在某些情况下，可能知道一个值的键名将出现在Request集合中，但不能准确地知道是哪一个集合。例如，假如有几个页面（或一个页面的不同段）发送一个值给同一个ASP脚本，它可能在Form或者QueryString集合中出现。
本章后面部分将研究Form和QueryString集合的差异。
要看一下一个值为什么可能出现在不同的集合中，考虑一下这种情况：使用了<A>超级链接元素请求一个页面。在这种情况下，增加一个值到请求的唯一方法是把它加到URL上。然而，同样的值可能已出现在另一个页面的<FORM>中，或同一页面不同部分：
...
<FORM ACTION=”process_page.asp” METHOD=”POST”>
<INPUT TYPE=”SUBMIT” NAME=”page” VALUE=”Next”>
<INPUT TYPE=”SUBMIT” NAME=”page” VALUE=”Previous”>
<INPUT TYPE=”SUBMIT” NAME=”page” VALUE=”Help”>
</FORM>
...
...
For help go to the <A HREF=”process_page.asp?page=Help”>Help Page</A>
...
在这种情况下，按下窗体上的Help按钮，将发送Request.Form集合中一对名称/值“page=Help”。然而，按下<A>超级链接也可能发送名称/值“Page=Help”，但是这次却是在QueryString集合里。为访问这个值，可使用ASP Request对象的一个特殊功能：
strPage = Request(“page”)
这将按序搜索全部的集合——QueryString、Form、Cookies、ClientCertificate、ServerVariables，直到发现第一个匹配值的名称。这样做比直接访问适当的集合效率低，并且是不安全的，除非能绝对保证这个值不会出现在另外一个集合中。
例如，可能希望搜集满足客户请求的Web服务器的名称，这通过出现在每个查询中的Request.ServerVariables集合中寻找“SERVER_NAME”来实现。然而，假如任一其他的集合也包含名为“server_name”的值（记住键名不区分大小写），当使用Request(“server_name”)时，得到的是错误的结果。使用Reqeust.ServerVariables(“server_name”)句法，我们将很难进行错误追踪。
总而言之，使用“搜索全部集合”技术要格外小心，且只在没有其他技术能够提供你需要的结果时使用。
g) 访问其他的集合
本章的这一节里，已经集中讨论了Form集合，这可能是使用得最多的一个。然而，所有这些技术同样适用于其他的对象。包括那些由Request对象提供的（即Form、QueryString、Cookies、ServerVariables和ClientCertificate）集合，及由Response对象提供的cookies（及将在下两章遇到的其他对象提供的集合）。
我们将简短了解一个值如何进入一个QueryString集合，及其优点和不足。然而，同时这两个Cookies集合有额外的功能，可以使使用cookie更加方便，下面讨论这个内容。


ASP 3.0高级编程（八）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（八）

3.3 ASP的Application对象和Session对象
本章已经讨论了两个ASP对象：Application对象和Session对象，因此能够访问Application对象和Session对象提供的集合、方法、属性和事件。本节将从程序设计的角度对这两个对象进行研究。
· 当载入ASP DLL并响应对一个ASP网页的第一个请求时，创建Application对象。该对象提供一个存储场所，用来存储对于所有访问者打开的所有网页都可用的变量和对象。
· 当访问者首次从站点请求一个ASP页面时，为他创建一个Session对象，并保持有效直到缺省的超时周期（或由脚本确定的超时周期）。该对象提供一个存储场所，用来存储仅仅对该访问者在会话的活动期间打开的网页可用的变量和对象。
图3-12（即图1-20）给出了用户的请求和服务器的响应在会话中的分布情况。所有的会话都在ASP应用程序中。

图3-12 ASP对象关系

3.3.1 ASP的Application对象成员概述
本节说明Application对象的集合、方法和事件（Application对象没有属性）。在下一节对Session对象（具有属性）进行同样的说明。然后将继续探讨使用这些对象所完成的任务，更详细地说明每个对象的各成员如何进行工作。
1． Application对象的集合
Application对象提供了两个集合，可以用来访问存储于全局应用程序空间中的变量和对象。集合及说明如表3-3所示：
表3-3 Application对象的集合及说明
集 合
说 明

Contents
没有使用元素定义的存储于Application对象中的所有变量（及它们的值）的一个集合。包括Variant数组和Variant类型对象实例的引用

StaticObjects
使用元素定义的存储于Application对象中的所有变量（及它们的值）的一个集合

2． Application对象的方法
Application对象的方法允许删除全局应用程序空间中的值，控制在该空间内对变量的并发访问。方法及说明如表3-4所示：
表3-4 Application对象的方法及说明
方 法
说 明

Contents.Remove(“variable_name”)
从Application.Content集合中删除一个名为variable_name的变量

Contents.RemoveAll()
从Application.Content集合中删除所有变量

Lock()
锁定Application对象，使得只有当前的ASP页面对内容能够进行访问。用于确保通过允许两个用户同时地读取和修改该值的方法而进行的并发操作不会破坏内容

Unlock()
解除对在Application对象上的ASP网页的锁定

注意，在运行期间不能从Application.StaticObjects集合中删除变量。
3． Application对象的事件
Application对象提供了在它启动和结束时触发的两个事件，如表3-5所示：
表3-5 Application对象的事件及说明
事 件
说 明

OnStart
当ASP启动时触发，在用户请求的网页执行之前和任何用户创建Session对象之前。用于初始化变量、创建对象或运行其他代码

OnEnd
当ASP应用程序结束时触发。在最后一个用户会话已经结束并且该会话的OnEnd事件中的所有代码已经执行之后发生。其结束时，应用程序中存在的所有变量被取消

3.3.2 ASP的Session对象成员概述
本节概述Session对象的所有成员。
1． Session对象的集合
Session对象提供了两个集合，可以用来访问存储于用户的局部会话空间中的变量和对象。这些集合及说明如表3-6所示：
表3-6 Session对象的集合及说明
集 合
说 明

Contents
存储于这个特定Session对象中的所有变量和其值的一个集合，并且这些变量和值没有使用元素进行定义。包括Variant数组和Variant类型对象实例的引用

StaticObjects
通过使用元素定义的、存储于这个Session对象中的所有变量的一个集合

2． Session对象的特性
Session对象提供了四个属性。这些属性及说明如表3-7所示：
表3-7 Session对象的属性及说明
属 性
说 明

CodePage
读/写。整型。定义用于在浏览器中显示页内容的代码页（Code Page）。代码页是字符集的数字值，不同的语言和场所可能使用不同的代码页。例如，ANSI代码页1252用于美国英语和大多数欧洲语言。代码页932用于日文字

LCID
读/写。整型。定义发送给浏览器的页面地区标识（LCID）。LCID是唯一地标识地区的一个国际标准缩写，例如，2057定义当前地区的货币符号是’£’。LCID也可用于FormatCurrency等语句中，只要其中有一个可选的LCID参数。LCID也可在ASP处理指令中设置，并优先于会话的LCID属性中的设置。本章后面提供一个ASP处理指令的列表

SessionID
只读。长整型。返回这个会话的会话标识符，创建会话时，该标识符由服务器产生。只在父Application对象的生存期内是唯一的，因此当一个新的应用程序启动时可重新使用

Timeout
读/写。整型。为这个会话定义以分钟为单位的超时周期。如果用户在超时周期内没有进行刷新或请求一个网页，该会话结束。在各网页中根据需要可以修改。缺省值是10min。在使用率高的站点上该时间应更短

3． Session对象的方法
Session对象允许从用户级的会话空间删除指定值，并根据需要终止会话。Seesion对象的方法及说明如表3-8所示：
表3-8 Session对象的方法及说明
方 法
说 明

Contents.Remove(“variable_name”)
从Session.Content集合中删除一个名为variable_name的变量

Contents.RemoveAll()
从Session.Content集合中删除所有变量

Abandon()
当网页的执行完成时，结束当前用户会话并撤消当前Session对象。但即使在调用该方法以后，仍可访问该页中的当前会话的变量。当用户请求下一个页面时将启动一个新的会话，并建立一个新的Session对象（如果存在的话）

注意，在运行期间不能从Session.StaticObjects集合中删除变量。
4． Session对象的事件
Session对象提供了在启动和结束时触发的两个事件，如表3-9所示：
表3-9 Session对象的事件及说明
事 件
说 明

OnStart
当ASP用户会话启动时触发，在用户请求的网页执行之前。用于初始化变量、创建对象或运行其他代码。

OnEnd
当ASP用户会话结束时触发。从用户对应用程序的最后一个页面请求开始，如果已经超出预定的会话超时周期则触发该事件。当会话结束时，取消该会话中的所有变量。在代码中使用Abandon方法结束ASP用户会话时，也触发该事件

3.3.3 使用Application和Session的事件
ASP的Application和Session对象体现了其他ASP内置对象所没有的特征——事件。然而，正像在前面的对象成员表中看到的那样，这些都是ASP会话和应用程序的工作相联系的事件。
1． Application和Session的事件处理器
每当一个应用程序或会话启动或结束时，ASP触发一个事件。可以通过在一个特殊的文件中编写普通的脚本代码来检测和应答这些事件，这个文件名为global.asa，位于一个应用程序的根目录中（对于缺省的Web网站是\InetPub\WWWRoot目录，或是作为一个实际应用程序定义的一个文件夹）。这个文件可以包含一个或多个HTML的元素，用于创建将在该应用程序或用户会话内使用的组件实例。
在第4章中将详细地介绍如何创建组件实例。下面的代码是global.asa文件的一个例子。我们只关注元素以及以Set关键字开始的那些代码行：

PROGID=”MSWC.Counters”>

PROGID=”MSWC.NextLink”>

因为这个global.asa文件用于本章中的示例页面，所以将需要将该文件放到Web网站的根目录中，或者放到已配置为一个虚拟应用程序的目录中，并且在该目录中包含有其他示例文件。
读取和存储值
注意上面的例子怎样读取Application和Session的变量，与在Request和Response对象的集合中所采取的方式相同。设置这些变量的值：
Application(“variable_name”) = variable_value
Application(“variable_name”) = variant_array_variable_name
Set Application(“variable_name”) = object_reference
获取这些变量的值：
variable_value = Application(“variable_name”)
variant_array_variable = Application(“variable_name”)
Set object_reference = Application(“variable_name”)
当然，对于Session对象可采取同样的方法。
可以看到，当从一个Session事件处理器访问时，怎样“锁定”（Lock）和“解锁”（unlock）该Application对象；当从一个ASP网页访问时，需要进行相同的处理。用Application事件内的代码访问Application对象中的值时，不要求这么做。这是因为在任何应用程序中只有一个Application对象的实例，并且其事件处理器的代码只在没有活动的用户会话时进行。
也可以看到一个基本的用户会话计数器是如何实现的。这里使用一个应用程序级的变量Visit_count，当新的会话启动时它就自动增加。 一般也不限制简单地把值保存到Application或Session对象中。例如，Web开发者的Web站点在http://webdev.wrox.co.uk上，有相应的一个global.asa文件，当一个新的会话启动时该文件就在服务器上的数据库中写入相应的条目，数据细节从Request.ServerVariables集合中获取。这提供了一个基本的方法统计访问者的数量，并收集访问者的一些基本信息。


ASP 3.0高级编程（六）

ASP 3.0高级编程（一）
ASP 3.0高级编程（二）
ASP 3.0高级编程（三）
ASP 3.0高级编程（四）
ASP 3.0高级编程（五）
ASP 3.0高级编程（六）
ASP 3.0高级编程（七）
ASP 3.0高级编程（八）
ASP 3.0高级编程（九）
ASP 3.0高级编程（十）
ASP 3.0高级编程（十一）
ASP 3.0高级编程（十二）
ASP 3.0高级编程（十三）
ASP 3.0高级编程（十四）
ASP 3.0高级编程（十五）
ASP 3.0高级编程（十六）
ASP 3.0高级编程（十七）
ASP 3.0高级编程（十八）
ASP 3.0高级编程（十九）
ASP 3.0高级编程（二十）
ASP 3.0高级编程（二十一）
ASP 3.0高级编程（二十二）
ASP 3.0高级编程（二十三）
ASP 3.0高级编程（二十四）
ASP 3.0高级编程（二十五）
ASP 3.0高级编程（二十六）
ASP 3.0高级编程（二十七）
ASP 3.0高级编程（二十八）
ASP 3.0高级编程（二十九）
ASP 3.0高级编程（三十）
ASP 3.0高级编程（三十一）
ASP 3.0高级编程（三十二）
ASP 3.0高级编程（三十三）
ASP 3.0高级编程（三十四）
ASP 3.0高级编程（三十五）
ASP 3.0高级编程（三十六）
ASP 3.0高级编程（三十七）
ASP 3.0高级编程（三十八）
ASP 3.0高级编程（三十九）
ASP 3.0高级编程（四十）
ASP 3.0高级编程（四十一）
ASP 3.0高级编程（四十二）
ASP 3.0高级编程（四十三）
ASP 3.0高级编程（四十四）
ASP 3.0高级编程（四十五）
ASP 3.0高级编程（四十六）

ASP 3.0高级编程（六）

第3章 ASP应用程序与会话

在前面的章节中介绍了ASP提供的访问一个客户请求和产生响应的方法，本章将讨论ASP的另两个对象。就是Application和Session对象。这两个对象不是直接地与请求和响应的管理有关，而是更多地与ASP网页运行环境的管理相关。
与建立Web站点或Web应用程序有关的共同问题之一，是使用HTTP协议时没有状态。状态提供了与一个指定用户有关的变量值、对象和其他资源，并且应用程序中的任意例程都能使用它；以一种像VB或C++这样的程序设计语言编写一般的基于客户的应用程序时，使用状态可以完成一些相应的工作。然而，Web并不提供这种能力。在本章中，读者将看到为什么和如何避免这个问题。
本章还涉及到一些术语和技术问题。它迄今为止，本书中已经简单地讨论了“Web应用程序”，但没有真正确切地理解或准确定义它们到底是什么。本书也涉及到了“用户会话”，也没有相应的比较完全的描述。前面有意地省略这方面的内容，因为它们与ASP的应用程序和会话密切相关。下面将介绍ASP的Application和Session对象。
本章研究的主要内容：
· Web应用程序是什么，以及它们如何与ASP Application对象相联系。
· ASP如何自动地创建和管理应用程序和会话。
· Application和Session对象提供的功能。
· 如何把Application和Seesion对象放入ASP网页中。
首先研究整个内容的核心问题：状态。

3.1 Web上的状态管理
许多开发人员把应用程序传送到Web之前从来没考虑状态的概念。正如前面说过的，Web是一个无状态的环境。因此应该探讨一下状态是什么，了解能够避免产生问题的方法。

3.1.1状态的准确定义
在单用户程序中，创建一个可执行的应用程序时，例如使用VB建立一个.exe文件，可以声明一个全局（或Public）变量，然后在代码中任何地方可对其进行访问。在应用程序运行的所有时刻，时刻值一直是有效，并且是可访问的。
对于一个传统的客户机/服务器解决方案，例如一个基于客户机的应用程序对一个基于服务器的数据库引擎进行访问的系统，每个客户端建立了一个与服务器和数据库应用程序的连接。这种连接通常是通过验证用户的方法来建立的。
验证过程是典型的识别用户身份的过程，通过一个用户名和口令组合来证明是否为合法的用户。
一旦通过验证，在客户端和基于服务器的应用程序之间就建立了连接，该连接在用户使用该应用程序的所有时间内一直保持有效。当用户注册到酵Windows 2000服务器上时，这一切便会发生。无论何时，管理员使用“Active Directory Users and Computers”实用程序（单击“Start”菜单的“Administrative Tools”选项中的“Directory Management”项）都可以观察到活动的用户连接。这个过程在许多系统中都相同，例如Microsoft SQL Server。
这种永久的连接意味着：当用户发送指令或请求到服务器上时，服务器会很容易地识别每个用户。同样服务器的响应或任何其他用户的信息也能直接返回用户。要进一步指出的是服务器可以比较容易地存储与每个客户相关的值和信息，并在需要的时候提供给相应的客户。当然，服务器应用程序能够拥有主全局变量，以便于用户在需要的时候进行访问。
这种识别每个客户端的请求并在内存中保存相关用户的值的能力构成状态。可以认为状态代表应用程序的值、环境以及用户的内部变量，并贯穿于应用程序和用户连接的整个过程。

3.1.2 状态的重要性
如果打算创建与用户进行交互的基于Web站点的应用程序，而不是仅显示独立页面的Web网站，必须能够为每个用户提供独立的状态。这可能只是记住他们的名字，也可能要为每个用户存储对象引用或不同的记录集。如果不能这样做，ASP网页就不能做更多的事情，因为该页面执行完成时，页面中的变量和其他相关资料都破坏了。录用户请求下一个页面时，这个页面提供的所有信息将全部失去。
因此，需要找到一种方法，保存每个访问者的状态。能够存储对所有用户而言的全局值是非常重要。例如，一个Web风格的访问或页面点击计数器，它不为每个用户提供自己的计数器，用户们通常想要看到访问者的总数，而不仅仅是他们自己访问的次数。访问者的数目需要与应用程序级状态一起存储，而不是与用户级状态一起存储。
这不是一个刚出现的问题，自从商用站点占据了Web，就已经存在，甚至更早些。所以已有许多在Web上存储状态的传统的解决方案。Web站点管理员想要了解访问者以前是否曾访问过他们的网站，如果访问过，访问过多少次？还定期访问其他什么网站等。这样可以更好地制定其广告目标。所有这些都要求一种方法来存储有关用户在访问时所产生的网页请求或每次访问间的信息。

3.1.3在Web上创建状态
在页面请求和站点访问之间提供状态常用的方法是通过cookie。我们在前面的章节中已经看到，如何在客户端的计算机中存放相应的值，这些值与每个页面请求一起发送给对此cookie有效的域。通过用ASP检查和更新cookie，在某种程度上能够保持一个状态。可以使用所包含的信息来识别用户，然后把用户连接到一个已存储相应值的集合。
例如，可以检测一个用户请求是否包含一个站点指定的cookie。如果不包含，则为该用户分配一个某种类型的标识，指明一个数量，并存储在带有一个长有效期的cookie中。以后该用户对这个站点的每一次访问，都能够检测到cookie并更新所包含的信息。同时可以收集有关访问的次数和持续时间的数据，并存储在服务器上，以备将来使用。
但是，如果用户转移到另一个计算机，或删除了cookie，或者他们的浏览器拒绝接收发送给他们的cookie，会发生什么事情呢？在这种情况下，不能维持状态，因为下一次不能识别他们现在，Web上有许多cookie，大多数人会接受它们，而不加理会。如果打开浏览器中的“Warn before accepting cookies”选项，接着漫游几个大的站点，你就会明白其中的含意。
1． 匿名访问者与授权的访问者
如果认为cookie是一个有点草率的解决方案，可以使用更直接的方法。许多站点采用的一种方法是，在访问者点击一个站点时，或者点击一个要求验证身份的页面时，弹出一个进行登录的对话框。访问者首先必须进行注册，获得一个某种类型的用户名/口令的组合，才能允许访问相应的站点或页面。
为了证实访问者是一个已知的并且合法的用户，在访问者的计算机上放置的一个cookie，它或者保存注册的详细数据，或者是一把表明已验证过身份的“钥匙（key）”。同时，访问者的详细数据永久地保存在服务器上，准备再次访问时使用。如果访问者的浏览器中有了这样一个cookie，他就可以自由地访问该网站，因为已经验证过了。
如果cookie没有有效期限（Expires），cookie的值在关闭浏览器时自动消失，在下一次访问时必须重新注册和再次验证。当然，如果拒绝接收cookie或删除了cookie，就只能再次得到注册对话框。这样的话，如果不被识别，就不能访问该站点。
通过强制用户就像注册到自己的网络一样注册到Web服务器，Windows 2000整体安全性能为IIS提供更强和更安全的验证功能。但是，这只能与Internet Explorer 3.0和之上版本的浏览器一起工作。IIS也可以使用BASIC验证允许非Microsoft浏览器注册Web服务器。
2． 不再有匿名访问者
在IIS Web服务器上使用ASP时，除非用户离开该站点到另一个网站或者关闭了浏览器，否则能在当前会话中跟踪用户。在本章的后面，将看到如何使用这个功能来标识一个访问者、存储用户的本地信息和提供状态。下面与已经讨论过的解决方案相比较，讨论其工作方式。
ASP和IIS共同提出了一个用户会话的概念，通过ASP Session对象进行交互。在每个访问者第一次访问服务器上的一个ASP网页时，为他创建一个新的并且独立的会话对象，分配给该会话一个会话标识号，并把包含会话标识符的特殊加密版本的一个cookie发送给客户。
cookie的路径（参看前面的章节有关cookie属性的描述）设置为运行在服务器上的ASP应用程序的根路径。这很可能上缺省的Web网站的根目录（即“/”），但也可能会是另外一个值（稍后会看到）。在cookie中没有提供Expires值，所以当浏览器关闭时，cookie值也就消失。
每当这个用户访问这个ASP网页，ASP都会查找这个cookie。命名为ASPSESSIONIDxxxxxxxx，其中每个x是一个字母字符。从第2章图2-7所示的ServerVariables集合，能够在HTTP报头中看到它。这里高亮地显示ASP cookie，如图3-1所示：

图3-1 显示的cookie值
但是，这个cookie不会出现在 Request.Cookies或Response.Cookies集合中，ASP把它隐藏起来，但仍保存在浏览器上。对于每个ASP网页请求，ASP都要查看该值。这个cookie包含的值，指明了这个用户的会话。因此，相应的Session对象（该对象在内存中已被处理，并且一直包含所有在前一页面请求过程中进行操作的值）的内容可以移交给ASP网页中的脚本。
当然，如前所述，如果客户浏览器不接收或不支持这些cookie，这个处理将失败。在这种情况下，不能创建ASP会话，对这个访问者的状态也不进行自动维护。

3.2 Web应用程序的定义
前面的章节中已经多次使用过Web应用程序（Web Application）术语，所指的既不是一个真正意义上的Web网站，又不是一个传统的应用程序。换句话说，而是认为它是一些Web网页和用来完成某些任务的其他资源的一个集合。它隐含这样一层意思：有一个预定义的路线贯穿于网页之中，用户可做出选择或提供信息使任务能够完成。
例如，一个在线商店，你为了购买货物，进行反复的观察和选择，浏览一系列网页，收集所需要的信息，支付相应的费用，最后发出定单。也可能是一个“软件升级向导”，指导用户完成下载和安装新软件的过程，或者可能是一个基于Intranet的报价单或销售报告的生成工具。
所有这些不同于“标准”的Web网站，一般的Web站点使用一系列菜单或导航栏以预先未定义的路径漫游该站点。但是一个Web应用程序远不只是受控制的导航器。自由地漫游于一个Web网站时，可以进行无状态的和匿名的访问，但Web应用程序一般不接受。

3.2.1 ASP应用程序的定义
上述内容可以认为是术语“Web应用程序”的一个合理的一般定义，但遗憾的是，在谈论有关“ASP应用”时，仅这些还不够。回答什么是“Web应用”可以是主观的，而回答什么是“ASP应用”则需要从技术上的解释。在ASP中术语“应用程序”有自己特定的含义，在讨论如何实现之前，弄懂这个概念至关重要。
ASP应用程序与两个主要的内容有关：
· 全局范围的规定，具有一个全局可访问的变量存储区域。
· 通过COM+与IIS的集成，可更好地管理组件。
下面讨论这些内容。第二个内容涉及到其他ASP对象，其覆盖范围相应广泛。在下一章研究ASP Server对象时，将对这部分内容进行详细地讨论。
1. 提供Web应用程序的全局范围
ASP提供一个Application对象，基本上与前面讨论的Session对象相当。但是，这是在应用程序层而不是在用户层。换句话说，该对象是全局的，不是对单独用户的，而是对应用程序的所有用户，其作用域不限制为单独用户的访问。这与在一个正常的可执行应用程序中的全局（或Public）变量相同。Application对象可用于在全局环境中存储变量和信息（即状态），该应用程序内的任何ASP网页中运行的脚本都可访问这些值，而不管是哪个访问者发出的请求。
但是，这没有回答主要问题：什么是一个ASP应用程序？为此，需要研究ASP内部的一些情况。
当用户请求一个ASP网页时，IIS通过实例化asp.dll（用来实现ASP）创建一个环境（如第1章所述）。将该页面解释为服务器端脚本，相应的脚本引擎的实例用来执行该脚本。
实例化的asp.dll初如事件启动一个ASP应用程序，创建一个Application对象。然后，为这个用户启动一个会话，并创建单独的Session对象。当更多的会话启动时，这个Application对象保留在作用域中（即已经实例化和可用）。一旦最后保持活动的会话结束，该应用程序就结束，并取消相应的Application对象。
(1) 缺省的ASP应用
Windows 2000在安装IIS和ASP时，创建一个缺省的Web站点。它被配置成一个ASP应用程序，涉及到在Properties对话框中针对站点根目录文件夹（缺省为C:\Inetpub\WWWRoot）的一些设置。图3-2所示是缺省Web站点的Properties对话框的屏幕。

图3-2 缺省Web网站的Properties对话框
涉及到ASP应用程序的文件之一是global.asa。这个文件用于定制应用程序行为的方式。放置在应用程序的根目录下，可用于该目录下的所有子目录。因此，如果它放置在整个Web站点的根目录下，则定义整个网站作为缺省的ASP应用程序的一部分。
在本章后面有关应用程序和会话事件的部分中，将看到这个文件及其使用方法。
(2) ASP虚拟应用程序
如同在设置过程中创建缺省的应用程序一样，可以在该Web网站的任何子目录中创建属于自己的ASP虚拟应用程序。这个应用程序包含作为“应用程序目录”而定义的目录中所有的子目录。并且，这个目录和子文件夹也都是缺省应用程序的一部分，共享由缺省的Application对象创建的全局空间。
事实上，在缺省的应用程序中存储的所有变量在子目录中的应用程序中也都是可用的。然而，如果该子目录应用程序中的一个ASP网页把一个值写入Application对象，而Application对象与缺省（根）应用程序中已存在的一个值有相同的名字，那么，原先的值在子目录应用程序中就不能再用。但是在其他的应用程序或ASP网页中，将保留原有的值，因为根目录的应用程序不能访问子目录应用中的值。
从一个子程序或函数的变量的角度考虑这个问题。如果定义一个变量intMyValue为Public或全局的变量，可以从任何的子程序或函数内部访问该变量。但是，如果又声明一个具有相同名字的局部变量，并在该子程序或函数内对该变量进行引用，则得到此变量的局部值。不能再访问原先的值。当子程序或函数结束，局部值被撤消，全局变量原有的值仍然保留着：
Public intMyValue = 42
Function DoSomething()
Response.Write intMyValue ‘Gives 42 from global variable
Dim intMyValue
IntMyValue = 17
Response.Write intMyValue ‘Gives 17 from new local variable,but
‘the global value of MyValue is still 42
End Function
(3) 创建自己的ASP虚拟应用程序
为了建立一个新的虚拟应用程序，使用Internet Services Manager应用程序或具有相同功能的HTML Web Manager网页。在Internet Services Manager中，在要创建的虚拟应用程序的目录上单击右键，并选择New，接着选择Virtual Directory，屏幕如图3-3所示：

图3-3 创建ASP虚拟应用程序的屏幕
这个操作启动New Virtual Directory Wizard，该向导的开始屏幕提供有关向导的操作信息。单击Next，在第二页中键入新的虚拟应用程序的名字（或别名）。这个名字与Internet Services Manager中选择的目录的路径联合起来，将成为该应用程序的URL。屏幕如图3-4所示：

图3-4 New Virtual Directory Wizard的屏幕
为了把一个现有的目录转换为与该目录具有相同名字的一个应用程序，选择包含想要转换的目录，并在向导的Virtual Directory Alias页中使用该目录名。例如把已有的test目录转换为一个虚拟应用程序，应该在Internet Services Manager中选择Default Web Site条目，并提供一个别名“test”。
再单击Next，指定包含该应用程序的内容（页面）的路径。单击Browser选择一个已有目录。这个目录是新的虚拟应用指向的目录。屏幕如图3-5所示：

图3-5 指定路径时的屏幕
单击Next打开Access Permissions页，选择给予这个应用程序的所有用户的权限。缺省值是Read和Run Scripts，对大多数用户而言是适合的。屏幕如图3-6所示：

图3-6 设置用户许可权限时的屏幕
如果想编写用户可直接执行的、定制的编译的CGI应用程序，只选择“Execute”：例如，用户通过在请求的URL中指定相应名字的方法执行的一个.exe文件，像“http://mysite.com/.../Test Application/create_user.exe?user=Jjones”。
单击Next，向导创建虚拟应用程序。在图3-7所示的屏幕中，可在左边的列表栏中看到带有一个包含一些填充物的打开的小盒子图标。

图3-7 虚拟应用程序创建完成后的屏幕
如果现在用右键单击新的应用程序并选择Properties，可以看到向导已经选择的设置。在这里可根据需要修改访问权限、“Local Path”和“Application Settings”。同时会看到一个Remove按钮，可以用来删除该虚拟应用程序，如图3-8所示：

图3-8 虚拟应用程序属性设置的屏幕