使 用MFC 开 发ISAPI Extensions 程 序.docx
《使 用MFC 开 发ISAPI Extensions 程 序.docx》由会员分享,可在线阅读,更多相关《使 用MFC 开 发ISAPI Extensions 程 序.docx(12页珍藏版)》请在冰豆网上搜索。
![使 用MFC 开 发ISAPI Extensions 程 序.docx](https://file1.bdocx.com/fileroot1/2023-1/27/5e45beaf-a96e-4b52-aca9-730ef448d717/5e45beaf-a96e-4b52-aca9-730ef448d7171.gif)
使用MFC开发ISAPIExtensions程序
使用MFC开发ISAPIExtensions程序
1.引言
----1.1ISAPI与CGI
----通用网关接口CommonGatewayInterface(CGI)很早就作为交
互式的Web应用程序的一个标准广泛应用在Internet之中。
CGI脚本允许人们用多种编程语言(如Basic、C、Perl、Shell等
等)来编写简单的应用程序。
这些脚本运行在Web服务器上,
而在客户的Web浏览器上输出运行结果。
客户的输入通过
环境变量或者标准输入设备来进行传递,然后CGI程序根
据需要完成特定的功能,并通过标准输出设备送回HTML格
式的结果显示在客户的浏览器中。
CGI的这一特性—设计
简单,再加上它支持多种编程语言,使得开发CGI应用程序
非常简单。
尽管如此,人们在使用中还是发现了CGI应用程
序的一个很大的缺点:
性能不高。
虽然有不少办法来使CGI应
用程序运行得更快一些(如把它们变成编译好的二进制代
码,而不用Perl脚本),但执行速度仍然是一个问题。
每当
通过Web访问一个CGI程序时,CGI执行文件(或者脚本的解释
器)都要为每一个请求创建一个新的进程。
对于一个信息
量比较大的站点来说,这无疑给服务器增加了一个沉重的负担。
----当微软开始开发自己的Web服务器(MicrosoftInternetInformation
Server或简称为IIS)时,意识到了CGI的这一大的缺陷,于是他
们就引入了ISAPI。
----ISAPI使用动态链接库DLL而不是可执行代码。
这些DLLs被
装入到服务器的内存空间。
由于代码在内存中缓存起来了,
而不是每次接收到请求时再装入到内存中,因此这一技术极
大地提高了交互式的Web应用程序的性能。
----ISAPI程序分为两种:
一种就是我们要介绍的ISAPIExtensions,
它提供了一种比CGI更好的实现方法;另一种称作ISAPIFilter,
它可以对服务器上进入或出去的数据进行过滤。
----总的来说,ISAPI优于CGI之处包括:
----①速度快:
ISAPI在性能上有很大的提高;
----②功能强:
ISAPI还可以创建Filter以对数据进行预处理。
并且它完全与MFC集成在一起了。
----相反,ISAPI的不足之处有:
----①标准化不够:
目前只有一部分服务器支持ISAPI;
----②开发困难:
相关资料很少,并且调试很麻烦。
----1.2ISAPI基础
----ISAs开发主要基于MFC的CHttpServer类。
该类控制了所
有与服务器的交互操作,同时还包含
了用于客户请求的所有函数。
尽管一个ISA只能有CHttpServer类
的一个实例,但
每个ISA仍然可以同时处理多个请求。
这是通过CHttpServer类为
每个请求创建
一个CHttpServerContext类来实现的。
CHttpServerContext类包含了每个
特定请求的所有数据以
及由ISA返回到客户的所有HTML代码。
----ISAPIDLLs的调用方法和CGI一样:
在客户端使用GET或POST方法。
例如,当客户作出下列请求时:
----
----"name"域和"id"域以及与它们相关的数据都被传递给ISA。
ISAPI在使用这些相关的数据之前把它们存放在相应的数据
结构中,这是通过一个请求映射系统来实现的。
----每一个请求都有一个解析映射表。
通过定义服务器从
客户接收的数据的类型和顺序,该解析映射表可以把数
据传递到合适的数据结构中。
例如,对于请求"name=fisherman&id=12345",
解析映射表将显示一个字符串和一个整型数,并且
"fisherman"and"12345"将被解析出来存放到各自的数据结构中。
----解析映射系统还有另外一个功能:
ISAPI可以把请求传
递给ISA内特定的成员函数。
请求字符串可以包含一个命令,
解析映射表就使用该命令把请求传递给ISA内正确的成员函数。
----由于ISAPI使用命令驱动机制来处理请求,因此在开始
开发ISA程序时可能会觉得有些麻烦,但是一旦学会了,用
户就会发现这是一个非常强大的处理请求的方法。
----2.使用MFC开发ISA程序
----2.1建立工程
----开发ISA的第一步工作是建立一个工程。
和创建其它
VisualC++(VC++)工程一样,创建ISA也有一个wizard来指导用
户完成初始的步骤。
打开VC++,在File菜单中选择New,然后
在对话框中选择Projects面板,在下面的列表中选择
"ISAPIExtensionWizard"工程类型,选择适当的路径,并把它
命名为"HelloWeb",如图1所示。
图1 New对话框
----接着选择Ok按钮,于是出现一个对话框让用户选择预
创建的ISAPI程序类型,缺省情况下是ISA程序,同时MFC被设
置为动态链接。
如果用户开发的服务器上有了这些MFCDLLs,
这当然是可以的。
但是如果没有安装DeveloperStudio,通常情
况下这些DLLs是没有的,这样用户的ISA将无法运行。
此时应
该把工程设置为静态链接。
我们建议用户这样设置,如图2
所示。
图2 ISAPIExtensionwizard对话框
----接着选择Finish按钮。
VC++将显示一个对话框给用户一些
关于新工程的提示信息。
在此对话框中选择OK。
----现在工程已经创建好了,现在该处理一些复杂点的问
题了。
我们在前面提到过,ISA在运行时是IIS的一部分,而IIS
又作为NT的一个服务而运行。
这一事实使用调试过程变得
复杂了,因为在IIS运行时,VC++的调试器不能够接管ISA。
为
了解决这个问题,微软公司以两种形式发行了IIS:
作为一
项服务,以及作为一个单独的可执行程序。
对于后一种情
况,我们就可以在命令行上来控制服务器。
虽然这样可以
解决上述问题并使得开发过程变得容易一些,但实现起来
显得很繁琐。
下面我们来介绍这个过程。
----当用户处于debug调试模式时,VC++(以及IIS)将在用户的
帐号和权限下运行。
由于通常IIS完成的一些工作是不允许
大多数用户有相应的权限的,因此用户(或用户的系统管
理员)需要做以下工作:
----①在桌面上选择“开始\程序\管理工具(公用)\域用户
管理器”,打开域用户管理器;
----②在“规则”菜单中选择“用户权限”;
----③选择“显示高级用户权限”检查框;
----④在“权限”下拉列表中选择“以操作系统方式操作”;
----⑤选择“添加”按钮得到“添加用户及组”对话框,选择
“显示用户”按钮,并在“名称”列表中选择用户使用的帐
号,然后选择“添加”按钮;
----⑥选择“确定”按钮;
----⑦对“产生安全审核”权限重复上述步骤。
----为了使这些设置生效,用户必须先退出登录,然后再登
录回来。
----IIS中包含了三项服务:
FTPPublishingService,GopherPublishing
Service和WorldWideWeb。
由于调试器要在命令行上运行IIS,所以
所有这三项服务都必须停止。
这可以通过“控制面板”中的
“服务”程序或者使用IIS的“Internet服务管理器”来实现。
如
果需要进行大量的调试工作,我们建议用户通过“控制面板”
中的“服务”程序来关闭IIS服务并禁止它们自动启动,这样可
以避免用户每次启动计算机时都要进行关闭服务的操作。
----接下来就必须对工程进行一些配置了:
----①在Project菜单中选择Settings菜单项;
----②选择Debug面板,并在Category下拉列表中选择General;
----③在Executablefordebugsession框中输入或者寻找IIS执行文
件的路径(通常情况下位于WINNT\system32\inetsrv\inetinfo.exe);
----④在Programarguments框中输入-ew3svc,如图3所示;
图3 Debug面板设置
----⑤选择Link面板;
----⑥在Outputfilename框中输入被编译后的DLL将被放置的路径
和文件名。
这个路径必须位于Web服务器的根目录下或者某个
虚拟目标下,以便客户可以通过URL来访问。
例如,我们的Web
服务器的根目录是c:
\InetPub\wwwroot\,我们把helloweb.dll放置在该
目录下,这样客户就可以使用下面的URL来访问它:
----
----如果用户现在还没有退出登录以改变权限,请现在行动,
然后再登录回来。
----ISAPIExtensionWizard所产生的缺省代码已经包含了一个可
工作的ISA所需要的一切,虽然该ISA还没有任何功能,但我们
不妨先试着进行编译一下。
----按下F5键以运行ISA,如果弹出对话框问是否创建工程时
选择Yes。
几秒钟之后调试器就已经工作了,此时IIS应该运行
在后台。
现在一切就绪了,用户可以在自己喜欢的浏览器中
输入上面所提到的URL,并在最后面加上一个问号?
,如下所示:
----
----请注意把域名换成正确的值。
----第一次连接到ISA时可能要花几秒钟的时间,此后该DLL被
缓存到了内存中,连接速度就会显著提高。
当DLL被装入后,
在浏览器中应该显示如下信息:
----ThisdefaultmessagewasproducedbytheInternetServerDLLWizard.EdityourCHelloWebExtension:
:
Default()implementationtochangeit.
----我们的第一个ISA已经工作了。
----2.2分析源代码
----一个ISA包含两个主要组成部分:
解析映射表和命令处理函数。
----当一个请求来自EXTENSION_CONTROL_BLOCK(此结构用于在服务器
和ISA之间进行通信)时,它被传递到命令解析映射表。
解析映
射表由一些宏组成,如HelloWeb工程(HELLOWEB.CPP)中的:
BEGIN_PARSE_MAP(CHelloWebExtension,CHttpServer)
//TODO:
insertyourON_PARSE_COMMAND()and
//ON_PARSE_COMMAND_PARAMS()heretohookupyourcommands.
//Forexample:
ON_PARSE_COMMAND(Default,CHelloWebExtension,ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default,CHelloWebExtension)
END_PARSE_MAP(CHelloWebExtension)
----BEGIN_PARSE_MAP宏标志着解析映射表的开始,它以ISA的
CHttpServer类和基类作为参数;ON_PARSE_COMMAND宏则把一个特
定的请求或命令映射到一个命令处理函数中。
它的参数
包括命令处理函数名、函数的类以及请求的格式;
DEFAULT_PARSE_COMMAND宏确定了当请求为空或者与解析映射表
不匹配时调用的函数。
它的参数包括函数名和函数的类。
----命令处理函数是解析映射表中调用的主CHttpServer类的
成员函数。
下面就是HelloWeb工程中的Default命令处理函数:
voidCHelloWebExtension:
:
Default(CHttpServerContext*pCtxt)
{
StartContent(pCtxt);
WriteTitle(pCtxt);
*pCtxt<<_T("ThisdefaultmessagewasproducedbytheInternet");
*pCtxt<<_T("ServerDLLWizard.EdityourCHelloWebExtension:
:
Default()");
*pCtxt<<_T("implementationtochangeit.\r\n");EndContent(pCtxt);}
----当请求为空或者包含Default时此函数被调用。
首先它
通过参数得到请求的CHttpServerContext(命令处理函数的第
一个参数必须是一个CHttpServerContext),StartContent把
标志放到pCtxt中,然后WriteTitle放置
----2.3修改HelloWeb
----下面我们将把缺省的消息换成"我会编ISAPI程序了!
"。
----找到CHelloWebExtension类的成员函数Default,修改成如下形式:
voidCHelloWebExtension:
:
Default(CHttpServerContext*pCtxt)
{
StartContent(pCtxt);
WriteTitle(pCtxt);
*pCtxt<<_T("我会编ISAPI程序了!
");EndContent(pCtxt);}
----然后按照前面方法编译并运行该DLL,并在浏览器中重
装或者刷新URL,用户即可看到显示的消息已经变了。
----然而,如果用户看到"ServerError500:
Specifiedmodulenotfound."
这样的错误信息,则表示用户的工程是动态链接的,而所必需
的DLLs不存在。
为了修正这个错误,需要到Project菜单中选择
Settings命令,然后选择General面板,在MicrosoftFoundationClasses
下拉列表种选择UseMFCinaStaticLibrary,然后重新编译该工程。
----3.深入理解ISA编程
----3.1 进一步分析解析映射表
----在解析映射表中使用了五个宏:
----BEGIN_PARSE_MAP:
开始解析映射表的定义;
----ON_PARSE_COMMAND:
解析客户的命令;
----ON_PARSE_COMMAND_PARAMS:
把请求的数据映射到相应的数据结构中;
----DEFAULT_PARSE_COMMAND:
定义缺省命令;
----END_PARSE_MAP:
结束解析映射表的定义。
----我们在来看一看HelloWeb中的解析映射表:
BEGIN_PARSE_MAP(CHelloWebExtension,CHttpServer)
//TODO:
insertyourON_PARSE_COMMAND()and
//ON_PARSE_COMMAND_PARAMS()heretohookupyourcommands.
//Forexample:
ON_PARSE_COMMAND(Default,CHelloWebExtension,ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default,CHelloWebExtension)
END_PARSE_MAP(CHelloWebExtension)
----此映射表定义了两个命令:
一个空的请求和缺省的Default命令。
空的请求格式如下:
----
----它是由DEFAULT_PARSE_COMMAND宏处理的。
----然而,如果Default命令放在了问号的后面:
----