说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

DELPHI与INTERNET(一)

HTML文档下载 WORD文档下载 PDF文档下载
DELPHI与INTERNET(一)

这篇文章主要讲述如何使DELPHI和因特网配合工作。本文中将详述两个专门技术:

WININET:构建 FTP,HTTP 和 Gopher 用户端程序 ISAPI:扩充因特网信息服务,例如,获得服务器上的信息并把它们显示在浏览器上。

现今的计算机世界中,由于微软公司的因特网战略而掀起了一个巨大发展潮流。那些制作

CGI(公共网关接口)和第三方工具(即使是最基本的因特网工具)的日子将最终一去不复返了。对复杂的第三方工具的需求总是存在的,但现在程序员将会发现他们所需的大量的嵌入操作系统的因特网工具,简言之,并不需要进一步的投资,你就能够使用免费的DELPHI资源来:

*开发 WEB 浏览器

*运行 FTP,HTTP 和 Gopher ,在两个DELPHI应用软件之间或DELPHI应用软件和基于TCP

(传输控制协议)的服务器之间操作TCP

因为DELPHI能够轻松地调用Windows API,并且它支持OCX/ActiveX,因此微软的新战

略和我们的计划配合的恰到好处。微软生产工具,而DELPHI程序员获得收成!

在本篇中有些什么?

这篇文章中包含了三个大部分和一些小部分,有三个大主题:

*寻找资料:那里能搞到本文中提及的技术资料,而且包含了关于您所需的运行文中代码 的软硬件的简短说明。

*ISAPI:怎样使用ISAPI

*WININET:怎样使用WININET

在大多数情况下,本文中的ISAPI和WININET部分是完全独立的,您可以自由地选择阅读时的顺序。

查找资料,硬件和软件的要求

您需要一份Microsoft Windows NT 3.51 Server 或 NT 4.0 Server 的拷贝,其中应附有因特网信息服务文档,因为您需要甬道其中所提到的技术。这份文档应随NT Server4.0 附送,NT 3.51的用户可从微软的网址上下载。运行Windows NT,您的机器的最低配置应为486兼容,20兆以上内存。

您必须有另一台计算机装有网页浏览器。为使本文中的ISAPI部分能够顺利运行,第二台

机器必须能够运行所有支持网页浏览器的软件。如果在您的机器上运行的是Windows 95 或 Windows NT

那么本文中的WININET 代码片就能运行的最好。任何符合条件的网页浏览器在这种技术环境下都能够使用。

在1996年六月以后发布的Delphi2.0以上的版本中,有您所需的把Delphi连接到因特网上

的几乎全部资源。

如果您没有最新的Delphi版本[注:此处作者指的是2.0版本(译者)],那么您需要本文

档中提到的特殊文件,所有这些几乎都可以从万维网上免费获得[注:如果您正在使用Delphi2.0以上版本,则不许考虑(译者)]。所有本文中提到的技术在Delphi2.0环境下都能顺利工作,但在16位Delphi环境下则不一定能顺利工作。

如果您需要从万维网上下载信息,链接为:http://www.borland.com/TechInfo/delphi/i

ndex.html

[注:现在已经不存在了!:-(( (译者)]

Delphi2.0的新版本中附有 WININET.PAS 文档,如果你的拷贝中不包含它,那么上面那个

万维网节点可以为您提供。WININET.PAS包括为扩展微软视窗因特网所设计的变量清单、函数、类型和属性。这意味着您能够轻而易举地为您的应用程序增添FTP、HTTP和Gopher支持。微软公司的WININET.DLL是免费发布的,如果它不在您的Windows/System 或Windows/System32 目录下的话,您可以从微软公司那里得到它。下面是可获得WININET.H这个视窗帮助文件的万维网节点:

http://www.microsoft.com/intdev/sdk/docs/wininet/default.htm [注:好象也没了!: -( (译者)]

一般来说,微软因特网开发者的网上之家是微软节点的 INTDEV 部分。

除了WININET和ICP之外,另一个为Delphi支持的关键技术就是ISAP。正如微软公司文档中

所描述的,这项技术能使您“‘写入’服务器端的原本和过滤本,从而扩充微软因特网信息服务和其他ISAPI万维网服务”。

如果您需要找到关于ISAPI的描述,可以去:

http://www.microsoft.com/intdev/sdk/servapi.htm [注:上帝保佑您!;-) (译者)]

在本文最后,附加了一个名为HTTPEXT.PAS的关键的ISAPI文档的拷贝。

微软公司免费发布的因特网控制包(ICP)是一个OCX/ActiveX控制集,您可以在Delphi中

把它们拖放到应用程序上(Delphi2.0中包含这些控件)。他们提供了创建Delphi应用程序的即时支持,他们知道如何浏览网页、 如何应用FTP、WINSOCK和其他因特网技术。如果您的Delphi拷贝中没有包含这些控件,那么您在使用它们之前您应该把这些文档添加进Delphi所在的目录中的Lib目录下。这些文档位于上面提及的链接中的Borland的INDEX.HTML站点下。在本文中我没有提到ICP控件,但是任何对这项技术有兴趣的人应该明确确认他拥有这些

控件的拷贝。

您可以从我的站点下载我的Pascal应用文件,他们的名字是STRBOX.PAS 和 MATHBOX.PAS 。

经常察看一下这个站点上的关于本文提到的信息的更新情况是很有好处的。

在这里我假设读者对于Delphi和Object Pascal都很熟悉,并且读者对于因特网,HTML,

浏览器和万维网服务器有基本的了解。

ISAPI

ISAPI是一项很容易使用然而功能强大的技术,它能够让您扩充因特网信息服务的功能。

这项技术随WindowsNT 4.0附送,让您在您的服务器上建立WEB、FTP和GOPHER站点。同时这项技术与WindowsNT3.51 Server[注:指服务器版本,另一个版本是工作站版本(译者)]兼容。

在过去,扩充网页服务器的最佳办法是建立CGI应用程序。它们是强有力的工具,但是也

被他们的执行格式所限制[注:如PERL是解释执行的(译者)]。当您从浏览其上发出一个基于CGI的请求到服务器上时,这个CGI 应用程序将极有可能先被强制装入内存中,这会消耗很多时间。而且,在某些环境下, CGI技术显得稍微难用了一点。

ISAPI是一种通过写入DLLs[注:动态链结程序(译者)]从而替代CGI应用的方法。您也可

以通过ISAPI来写过滤文本,但这项技术我不会在本篇中提及。同CGI相比,ISAPI更容易使用,而且它更快,同时能更好地利用系统资源。在下面几点中,我将详细地介绍为什么ISAPI DLLs比CGI应用要更为出色:

ISAPI DLLs与HTTP服务位于相同的地址,因此他们能够从服务器上直接存取HTTP服务。与CGI应用相比,它们 能更快地装入内存;当他们在服务器上发出请求时,所需的停悬的时间[注:指发出请求到接受服务器应答的时间(译者)]要少的多。这点当服务器的负荷很重时更加重要。

您可以控制DLLs何时被装载和卸载。例如:您可以在第一次尝试请求时预先装载DLLs;当

它们不被使用时卸载 这个ISAPI应用DLLs以便释放系统资源。

正如前文所述,您可以利用ISAPI写过滤文本[注:一般指C/S结构中的脚本(译者)],更

具微软的文档,您可以通过ISAPI过滤文本做下面这些事情:

用户授权方案

压缩

加密

登入

通信分析或其他请求分析(例如,寻找 "..\..\etc\password" 中的请求)

在本文中,我会着重介绍如何编写返回数据集的DLLs,或者是如何与运行浏览器的用户进

行简单的联系。

ISAPI 基础

HTTPEXT.PAS文件包含了使用ISAPI的关键声明。这个文件应随1996年6月以后发表的

Delphi版本分发。它也可以在Borland的站点上找到,在本文的ISAPI部分附有这份文档。因为这是基于NT的技术, 您必须使用Delphi2.0以上的版本来应用这项技术。您不可能在16位的编辑器上应用它。

HTTPEXT.PAS包含了微软公司创立的ISAPI技术的接口[注:指Delphi接口,ISAPI由C++编

写(译者)]。在编写Delphi的时候并没有提供ISAPI的用户接口,我会仅仅就如何使用微软公司的现有技术进行描述。不过,ISAPI 太容易使用了,而且对大多数用户来说,用户的Delphi对象的版本并不是必须的。

有三个函数可作为ISAPI DLLs的入口,前两个是必须的,第三个时可选的。

GetExtensionVersion: 进对最低版本做检查

HttpExtensionProc: 这是DLL的入口,就象是Delphi应用程序中的 begin...end 块

TerminateExtension: 这是个可选的程序,它可以用作清除其他内存分配的线程。

当您在创建ISAPI DLL的时候,您必须引用上面列出的三个函数中的头两个函数,执行这

两个函数是所有ISAPI编程的关键。

这三个语句都包含了“字输出”,使用这项术语是因为ISAPI DLLs扩充了因特网信息服务

器。(记住,因特网信息服务器指的是微软服务器。如果您要把一台NT服务器作为体格网页服务器的话,那么,这正是您所需的工具。ISAPI DLLs随NT4.0分发,在安装操作系统是自动安装。)

ISAPI提供了一个制作服务器可遵循的标准。例如,它可以把网景公司的复杂的NSAPI接口

压缩至相关的简练而优美的ISAPI来对NSAPI接口进行操作。

下面是这两个重要函数的声明

function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;

function HttpExtensionProc(var ECB: TExtensionControlBlock): DWORD; stdcall;

您只要把GetExtensionVersion粘贴到您的DLLs救行了.当ISAPI向公众发布新版本时您只需要做轻微的改动。

function GetExtensionVersion(var Ver: THSE_VERSION_INFO):

BOOL; stdcall;

begin

Ver.dwExtensionVersion := $00010000; // 1.0 support

Ver.lpszExtensionDesc := 'Delphi 2.0 ISAPI DLL'; // Description

Result := True;

end;

The parameter passed to this function is declared in HTTPEXT.PAS as follows:

有关的参数在HTTPEXT.PAS中声明如下:

PHSE_VERSION_INFO = ^THSE_VERSION_INFO;

THSE_VERSION_INFO = packed record

dwExtensionVersion: DWORD;

lpszExtensionDesc: array[0..HseMaxExtDLLNameLen-1] of Char;

end;

常量HseMaxExtDllNameLen 在声明中的值为256。纪录中的这两个变量是“自声明”的, 前一个包含了ISAPI的版本号[注:即变量dwExtensionVersion (译者)],后一个则表示用户定义的一个用来描述DLLs的字符串。

在您引用GetExtensionVersion语句的同时,您必须在您的DLL程序的DPR文件部分增添输

出部分。在您写这段语句时您还应该写下:

exports

GetExtensionVersion

HttpExtensionProc;

这就是您在建立这两个重要ISAPI DLL的函数时所要做的。下一步,使用 HttpExtensionProc,稍微复杂一点,因此我将把它作为一个独立的部分。

与 HttpExtensionProc 一起工作

HttpExtensionProc语句是DLL的入口。它的作用就好比C语言中的 main() 语句,或者

Delphi 中的begin...end 部分

这里有一个简单的使用GetExtensionVersion语句的例子

function HttpExtensionProc(var ECB: TExtensionControlBlock):

DWORD; stdcall;

var

ResStr: string;

StrLen: Integer;

begin

ECB.lpszLogData := 'Delphi DLL Log';

ECB.dwHTTPStatusCode := 200;

ResStr := 'Test server result' +

'

Test server results

' +

'Hello from ISAPI
' +

'';

ResStr := Format(

'HTTP/1.0 200 OK'#13#10+

'Content-Type: text/html'#13#10+

'Content-Length: %d'#13#10+

'Content:'#13#10#13#10'%s'

[Length(ResStr)

ResStr]);

StrLen := Length(ResStr);

ECB.WriteClient(ECB.ConnID

Pointer(ResStr)

StrLen

0);

Result := HSE_STATUS_SUCCESS;

end;

如果您在浏览其中向这个DLL发出请求,那么您会得到一页这样的回应:

Test Server Results

Hello from ISAPI

函数体内的大部分域提供基本信息的简单的HTML代码密切相关。您还需要填写TExtensionControlBlock中的一些域,如下所示。

注意到在这个纪录里有一个叫做WriteClient的函数指针,您可以引用这个函数把信息传

送回浏览器。当呼叫这个函数时,您使用到了下面提到的TExtensionControl块中的ConnID字段。当函数被呼叫时,ConnID为您自动填充。

在察看函数的代码之前,请让我为您演示所有用到的上文提及的HttpExtensionProc函数

的ISAPI DLL的完整程序

library Isapi1;

library Isapi1;

uses

Windows

SysUtils

HTTPExt;

function GetExtensionVersion( var Ver: THSE_VERSION_INFO ): BOOL; stdcall;

begin

Ver.dwExtensionVersion := $00010000; // We're expecting version 1.0 support

Ver.lpszExtensionDesc := 'Written in Delphi 2.0';

Result := True;

end;

function HttpExtensionProc( var ECB: TEXTENSION_CONTROL_BLOCK ): DWORD;

stdcall;

var

ResStr: string;

StrLen: Integer;

begin

ECB.lpszLogData := 'Delphi DLL Log';

ECB.dwHTTPStatusCode := 200;

ResStr := '

' +

'

Test server results

' +

'

Isapi says hello to DevRel

';

ResStr := Format(

'HTTP/1.0 200 OK'#13#10+

'Content-Type: text/html'#13#10+

'Content-Length: %d'#13#10+

'Content:'#13#10#13#10'%s'

[Length(ResStr)

ResStr]);

StrLen := Length(ResStr);

ECB.WriteClient(ECB.ConnID

Pointer(ResStr)

StrLen

0);

Result := HSE_STATUS_SUCCESS;

end;

exports

GetExtensionVersion

HttpExtensionProc;

begin

end.

为了运行这个DLL程序,您应该把它复制到您的NT服务器下的脚本目录中去。在我的NT4.0 机器中

它就像这样:

c:\winnt\system32\inetsrv\scripts\mystuff\isapi1.dll

在这个例子中,我已经创建了我的名为“mystuff”的目录

它只不过是用来存储我创建的

ISAPI DLLs。您的目录,当然和我的机器上的不完全一样,取决于您的“inetsrv”目录位置和其它因素。

为成功调用这个DLL,您应该在您的HTML页上增添这个超链接:

ISAPI One

当用户点击这个超链接时,ISAPI1 Dll会被呼叫,然后字符串“Hello from ISAPI”会显

示在用户的浏览器上。如果您并不是把 ISAPI.DLL放在 mystuff 目录下,那么您应该修改上面的HTML代码来使之与您的情况适应。注意,您的目录必须与目录 inetsrv 有关,不应,也不能包含您的整个DLL所在的目录。

下面是呼叫的完整的HTML脚本:

CharlieC Home Page

My Home Page

This is the home page for my home computer.

ISAPI One

注意,如果您多次把程序ISAPI1.DLL复制到 mystuff 目录下,在每一次复制之前您应该

关掉网络服务器的万维网端口。这是因为,在第一次复制这个DLL时,您可以不受限制,但在此之后,它就属于服务器了。因此,在您复制第一次拷贝的更新版本时,因当关掉万维网服务。您可以使用网络管理程序来关掉万维网服务。这个程序应该在微软网络管理程序组(Microsoft Internet Server group)下面,在安装网络信息服务时被安装到程序管理器(Explorer/Program Manager)下。

与 TExtensionControlBlock 一起工作

通过本文中的这一要点,您能够建立您的第一个ISAPI DLL,并且能在第二台机器上的网

页浏览器调用它。

在本文中接下来的ISAPI的其余部分将会更加深入。

这里是HttpExtensionProc参数中比较复杂的部分

PExtensionControlBlock = ^TExtensionControlBlock;

TExtensionControlBlock = packed record

cbSize: DWORD; // = sizeof(TExtensionControlBlock)

dwVersion: DWORD; // version info of this spec

ConnID: HCONN; // Context Do not modify!

dwHttpStatusCode: DWORD; // HTTP Status code

// null terminated log info specific to this Extension DLL

lpszLogData: array [0..HSE_LOG_BUFFER_LEN-1] of Char;

lpszMethod: PChar; // REQUEST_METHOD

lpszQueryString: PChar; // QUERY_STRING

lpszPathInfo: PChar; // PATH_INFO

lpszPathTranslated: PChar; // PATH_TRANSLATED

cbTotalBytes: DWORD; // Total bytes from client

cbAvailable: DWORD; // Available number of bytes

lpbData: Pointer; // pointer to cbAvailable bytes

lpszContentType: PChar; // Content type of client data

GetServerVariable: TGetServerVariableProc;

WriteClient: TWriteClientProc;

ReadClient: TReadClientProc;

ServerSupportFunction: TServerSupportFunctionProc;

end;

注意到这个纪录中包含了上面提到过的ConnID字段,并且向 WriteClient 传送第一个参数。

这个纪录中的第一个参数是为版本控制而设的。它应该是TExtensionControlBlock的大小的规定。如果微软公司改变了它的结构,那么它们能够通过检查纪录的大小来判断它们正在处理的结构版本。 您永远也不要这个纪录中的前三个字段,它们早已被ISAPI填充,在您的程序中,它们只能被访问,而不能被改变。

这个纪录中最重要的字段可能就是lpszQueryString了,它包含了从服务器上传来的请求

的信息。例如,假设您已经创建了一个名叫 ISAPI1.Dll。为了调用这个DLL,您就要在您的浏览器的一页上创建一个像这样的HREF [注:HTML语言中的一种格式(译者)] :

Test One

如果您希望响应这个DLL,您就要对上面那行做这样的改动:

Test One

假如HTML代码段中有像上面两行中的第二行,那么,您的DLL就会在lpszQueryString参数

中得到“MyQuery” 的字符串,特别要注意跟在请求字符串后的请求标志的使用。

当然,您可以随心所欲地改变请求字符串。例如,您可以这样写:

Test One

在这个请求中,这个DLL会回答服务器的名称。您在传递这个参数时,不受任何限制。您

可以传递任何您想要的东西,而且,如何分析DLL中的信息也由您的喜好决定。

当您从服务器返回信息至浏览器时,您使用到了这个纪录中的“WriteClient”函数指针

。在初始化这个指针时您不需做任何事;它已经自动地有网络信息服务器传递给您了。

CGI应用程序的作者会注意到传送请求字符串的语法十分熟悉。事实上,ISAPI跟随了CGI

的大多数习惯,在TExtensionControlBlock中的多数字段可以简单地被CGI技术借用。

在TExtensionControlBlock中的另一个关键字段是 lpbData ,它包含了从浏览起上传给您的附加信息。

例如,您有一个伴随几个字段的HTML窗体,这些自断中包含的信息就会被一个叫做“

lpData”的指针传递。本文中的下一个主题,“从‘确认’按钮中获得信息”,将会着重讲述怎样处理这种情况。

到现在为止我已经介绍了TExtensionControlBlock中的四个关键字段:

WriteClient: 一个能够让您传递格式化的HTML数据到浏览器上的指针。这个函数用到了

TExtensionControlBlock的ConnID字段。

lpszQueryString: 从浏览骑上传来的请求。

lpbData: 从浏览器上传给你的人一的附加数据。通常是一个HTML窗体的任意字段的内容

。我将在“确认 按钮”这部分进一步讨论。

要获得其他TExtensionControlBlock中的字段是如何工作的感觉,最好的办法就是亲自在

浏览其中将他们做对照。换句话说,您会希望创建一个HTML页,使得用能够调用客户端的ISAPI DLL。这个ISAPI DLL的目的仅仅是在HTML中格式话TExtensionControlBlock中的每一个字段,然后把它们传回浏览器。这样就把您的浏览器变成了一个有点可怕的调试器,来显示TExtensionControlBlock中的所有字段。

这里有一个程序,由Borland公司的 Danny Thorpe 编写,他会执行这个任务:

library test1;

uses

Windows

SysUtils

HTTPExt;

function GetExtensionVersion( var Ver: THSE_VERSION_INFO ): BOOL; stdcall;

begin

Ver.dwExtensionVersion := $00010000; // 1.0 support

Ver.lpszExtensionDesc := 'A test DLL written in Delphi 2.0';

Result := True;

end;

function HttpExtensionProc( var ECB: TEXTENSION_CONTROL_BLOCK ):

DWORD; stdcall;

var

ResStr: string;

StrLen: Integer;

Buf: array [0..1024] of Char;

begin

ECB.lpszLogData := 'Delphi DLL Log';

ECB.dwHTTPStatusCode := 200;

ResStr := Format(

'Test server result' +

'

Test server results

' +

'Size = %d
'+

'Version = %.8x
'+

'ConnID = %.8x
'+

'Method = %s
' +

'Query = %s
' +

'PathInfo = %s
'+

'PathTranslated = %s
'+

'TotalBytes = %d
'+

'AvailableBytes = %d
'+

'ContentType = %s

'+

'

Some Server Variables

'

[ECB.cbSize

ECB.dwVersion

ECB.ConnID

ECB.lpszMethod

ECB.lpszQueryString

ECB.lpszPathInfo

ECB.lpszPathTranslated

ECB.cbTotalBytes

ECB.cbAvailable

ECB.lpszContentType]);

with ECB do

begin

StrLen := Sizeof(Buf);

GetServerVariable(ConnID

'REMOTE_ADDR'

@Buf

StrLen);

ResStr := ResStr + 'REMOTE_ADDR = '+Buf+'
';

StrLen := SizeOf(Buf);

GetServerVariable(ConnID

'REMOTE_HOST'

@Buf

StrLen);

ResStr := ResStr + 'Remote_Host = '+Buf+'
';

StrLen := SizeOf(Buf);

GetServerVariable(ConnID

'REMOTE_USER'

@Buf

StrLen);

ResStr := ResStr + 'Remote_User = '+Buf+'
';

StrLen := SizeOf(Buf);

GetServerVariable(ConnID

'SERVER_NAME'

@Buf

StrLen);

ResStr := ResStr + 'SERVER_NAME = '+Buf+'
';

StrLen := SizeOf(Buf);

GetServerVariable(ConnID

'SERVER_PORT'

@Buf

StrLen);

ResStr := ResStr + 'SERVER_PORT = '+Buf+'
';

StrLen := SizeOf(Buf);

GetServerVariable(ConnID

'SERVER_PROTOCOL'

@Buf

StrLen);

ResStr := ResStr + 'SERVER_PROTOCOL = '+Buf+'
';

StrLen := SizeOf(Buf);

GetServerVariable(ConnID

'SERVER_SOFTWARE'

@Buf

StrLen);

ResStr := Format('%sSERVER_SOFTWARE = %s
'+

'ThreadID = %.8x
'

[ResStr

Buf

GetCurrentThreadID]);

end;

ResStr := ResStr + '';

ResStr := Format(

'HTTP/1.0 200 OK'#13#10+

'Content-Type: text/html'#13#10+

'Content-Length: %d'#13#10+

'Content:'#13#10#13#10'%s'

[Length(ResStr)

ResStr]);

StrLen := Length(ResStr);

ECB.WriteClient(ECB.ConnID

Pointer(ResStr)

StrLen

0);

Result := HSE_STATUS_SUCCESS;

end;

xports

GetExtensionVersion

HttpExtensionProc;

egin

end.

为了调用这个DLL,您应该建立一个包括下面这行的 HRML 脚本

Test One

从“确认”按钮获得信息

通常向您发送信息的HTML窗体中都有一个确认按钮。只要信息量小于49KB,您就可以

认为TExetensionControlBlock中的 lpbData 字段是可用的。这里显示了您可以如何在大

多数情况下获得由这个字段的指针发来的信息:

var

S: string;

begin

S := PChar(ECB.lpbData);

end;

如果从这个字段传来的信息大于48KB,那么您必须呼叫 ReadClient 来获得其余的信息。

如果您想要确切地知道在 lpbData 字段中哪些信息是可用的,您可以使用下面两个函数把数据传回到您的网页浏览器中:

function SetUpResString: string;

begin

Result := '' +

'Test server result' +

'

Test server results

' +

'lpbData = %s ' +

'';

end;

function HttpExtensionProc(var ECB: TExtensionControlBlock):

DWORD; stdcall;

var

ResStr: string;

StrLen: Integer;

S

S1: string;

begin

ECB.lpszLogData := 'Delphi DLL Log';

ECB.dwHTTPStatusCode := 200;

ResStr := SetUpResString;

S := PChar(ECB.lpbData);

ResStr := Format(ResStr

[S]);

StrLen := Length(ResStr);

ECB.WriteClient(ECB.ConnID

Pointer(ResStr)

StrLen

0);

Result := HSE_STATUS_SUCCESS;

end;

假设您已经有了附有下面代码的HTML窗体:

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

Enter Number to Square:

MAXLENGTH="25" SIZE=25>

这段代码会产生一个包含一个供您输入数字的文本区和一个叫做“submit”按钮的窗体,按钮的名字叫做“GetSquare”。如果有了这个窗体,接着您可以预计上面的两段程序会返回如下的字符串,假设用户在窗体中的文本区输入了数字23:

lpbData = GetSquare=23&GetSquare=Submit

为了理解这时究竟发生了什么,注意一下从上面函数中摘录HTML语句中的主体部分, 这部分语句驻留在服务器上,反映如下:

'lpbData = %s ' +

如果您研究过上面 HttpExtensionProc 函数中的代码,您会发现就在这句之前,它使用了 Format 语句中的 %s 参数来代替了 ECB.lpbData 中的值。(如果您不清楚语句Format 是怎样工作的,请参阅有关的 Delphi 文档)[注:在作者所著的 Delphi2 编程大全(Delphi2

Unleashed)中的第三章《字符串与文本文件》中有详细说明(译者)]

假设上面所示的窗体中,当用户按下“确认”按钮时,lpbData 传递给ISAPI DLL的值是:

GetSquare=23&GetSquare=Submit

为了让您有清晰的概念,让我重复一下上面两个语句传回给浏览器的信息是下面的字符串,您已经看过了:

lpbData = GetSquare=23&GetSquare=Submit

观看这个过程的最好办法试运行下面列出的 ISAPI2 程序。 ISAPI2 和ISAPI1 差不多,但他包含了上面显示的新的 HttpExtensionProc 函数,并且它还包含了SetUpResString 这个实用函数。

library Isapi2;

uses

Windows

SysUtils

HTTPExt;

function GetExtensionVersion( var Ver: THSE_VERSION_INFO ):

BOOL; stdcall;

begin

Ver.dwExtensionVersion := $00010000; // 1.0 support

Ver.lpszExtensionDesc := 'DLL written in Delphi 2.0';

Result := True;

end;

function SetUpResString: string;

begin

Result := '' +

'Test server result' +

'

Test server results

' +

'lpbData = %s ' +

'';

end;

function HttpExtensionProc( var ECB: TEXTENSION_CONTROL_BLOCK ):

DWORD; stdcall;

var

ResStr: string;

StrLen: Integer;

S

S1: string;

Len: Integer;

begin

ECB.lpszLogData := 'Delphi DLL Log';

ECB.dwHTTPStatusCode := 200;

ResStr := SetUpResString;

S := PChar(ECB.lpbData);

ResStr := Format(ResStr

[S]);

StrLen := Length(ResStr);

ECB.WriteClient(ECB.ConnID

Pointer(ResStr)

StrLen

0);

Result := HSE_STATUS_SUCCESS;

end;

exports

GetExtensionVersion

HttpExtensionProc;

begin

end.

一旦您从窗体中获得了由 lpbData 变量传来的信息,您就能分析这些信息或者把它们返回给用户。比如说,您可以从上面例子中把数字23抽出来,做平方后返回用户。通过这样做可以使您从用户中获得信息,在这里是数字,对数字进行一些数学运算,最后把结果返回给用户。这意味着您可以在电波中创建互动的网页,这可是现在因特网编程中最流行的哦!

以下是一个通过网络提交数字平方给浏览器的完整的程序代码:

library Isapi3;

{ This code shows how to take input from the user via a browser

parse that information

and then return an answer to the user. In particular

the user submits a number

this code squares it

and then sends the result back to user. Here is the form from the browser that submits the information for parsing:

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

Enter Number to Square:

MAXLENGTH="25" SIZE=25>

}

uses

Windows

SysUtils

HTTPExt

StrBox;

function GetExtensionVersion( var Ver: THSE_VERSION_INFO ):

BOOL; stdcall;

begin

Ver.dwExtensionVersion := $00010000; // version 1.0 support

Ver.lpszExtensionDesc := 'ISAPI3.DLL';

Result := True;

end;

// Parse lpbData and retrieve the number the user passed to us.

function ParseData(S: string): Integer;

begin

S := StripLastToken(S

'&');

S := StripFirstToken(S

'=');

Result := StrToInt(S);

end;

function SetUpResString: string;

begin

Result := '' +

'Test server result' +

'

Test server results

' +

'Answer = %d ' +

'';

end;

function HttpExtensionProc( var ECB: TEXTENSION_CONTROL_BLOCK ):

DWORD; stdcall;

var

ResStr: string;

StrLen: Integer;

S

S1: string;

Num: Integer;

begin

ECB.lpszLogData := 'Delphi DLL Log';

ECB.dwHTTPStatusCode := 200;

ResStr := SetUpResString;

S := PChar(ECB.lpbData);

Num := ParseData(S);

Num := Sqr(Num);

ResStr := Format(ResStr

[Num]);

StrLen := Length(ResStr);

ECB.WriteClient(ECB.ConnID

Pointer(ResStr)

StrLen

0);

Result := HSE_STATUS_SUCCESS;

end;

exports

GetExtensionVersion

HttpExtensionProc;

begin

end.

这段代码从按下确认按钮的用户那里接受下面的字符串,用户要求平方后的数字:

GetSquare=5&GetSquare=Submit

假设这样输入,这段代码会通过因特网返回用户下面的字符串:

Answer = 25

一句话,用户输入数字5,你返回用户数字25。如果用户提交数字10,那么您返回数字100。这看起来微不足道,但在这里重要的是因特网上发生的行为[注:指互动网页(译

者)]

分析用户传来的函数像这样:

// Parse lpbData and retrieve the number the user passed to us.

function ParseData(S: string): Integer;

begin

S := StripLastToken(S

'&');

S := StripFirstToken(S

'=');

Result := StrToInt(S);

end;

这两个语句在单元中,在本文开头提到过,也包含在我的站点上。[注:这个文件在网络上几乎到处可见

您也可以向译者索取(译者)][ 在本篇文章中

关于ISAPI我只想谈这么多了。这些内容对于启发您利用这项优越的技术并获得乐趣来说应该是够用的了。接下来我要谈一下 GetServerVariable 、 ReadClient 这两个语句,在这方面我只进行了极其有限的试验。在本文中,我附加了HTTPEXT.PAS 文件,因为除了这分关键文档,在其他地方您不会找到它。

GetServerVariable 和 ReadClient 语句

正如您的CGI应用程序中的请求信息一样,您可以使用语句来从服务器上获得信息。 下面是呼叫这个语句的例子:

Len := HseMaxExtDllNameLen;

SetLength(S1

Len);

Dec(Len);

ECB.GetServerVariable(ECB.ConnID

'CONTENT_LENGTH'

PChar(S1)

Len);

首先,这段代码设定了保留从服务器上取得的信息的缓冲区的长度。接着它呼叫服务 器并发出请求,在本例中,它要求获得服务器传来的信息的"CONTENT_LENGTH"。

微软公司的文献告诉我们,您可以通过 GetServerVariable 的第二个参数来传递跟 着的字符串:

AUTH_TYPE 它包含了使用授权的类型。比如,如果使用的是基本(basic)授权,那么

字符串就是"basic";如果是 NT challenge 回应,字符串就是"NTLM"。其他的授权属尤其对应的字符串。因为不断有新的授权类型被增添到服务器上,列出所有可能的字符串是不可行的。如果字符串为空,那么并没有使用任何授权。

CONTENT_LENGTH 脚本预计从客户端回收到的字节数。

CONTENT_TYPE 由请求布告的主体部分提供的信息的内容类型。[注:小弟才疏学浅,a

POST request 暂译作"请求布告",望方家指正(译者)]

PATH_INFO 附加的路由信息,由客户机提供。它包含了跟在脚本名字之后的URL的漫游路

由。如果有的话,它在请求字符串的前面。

PATH_TRANSLATED 它是 PATH_INFO 的值,但包含了扩充到一个路径标志的所有虚拟路由的名字。

QUERY_STRING 跟在参考这个脚本的URL中的"?"后面的信息。

REMOTE_ADDR 发出请求的客户机或其代理商(例如,网关或防火墙)的IP地址。

REMOTE_HOST 发出请求的客户机或其代理商(例如,网关或防火墙)的主机名。

REMOTE_USER 它包含了由客户机提供并且由服务器授权的用户名。如果返回空串那么用户

使你名的(但是经过授权)。

UNMAPPED_REMOTE_USER 它是有如下特征的用户的名称:该用户向NT用户帐目发出请求(这是他以身份出现),在此之前ISAPI应用程序过滤起映射了该用户。

REQUEST_METHOD 是 HTTP 请求方法。

SCRIPT_NAME 执行的脚本程序名称。

SERVER_NAME 当它以自参考URLs形式出现时的主机名或IP地址。

SERVER_PORT 接受请求的TCP/IP的端口。

SERVER_PORT_SECURE 一个非0即1的字符串。当请求由安全端口处理时,它是1;否则是0 。

SERVER_PROTOCOL 接受与这个请求相关的协议的信息的名称和版本。他通常是 HTTP/1.0 。

SERVER_SOFTWARE 是ISAPI应用DLL程序运行时所在的网页服务器的名称和版本。 ALL_HTTP 先前的变量并没有分析全部的HTTP字段头。这些变量从HTTP_<字段头名>中得出 。字段头(由行标分离)包含了各自的字符串,这些字符串并不会终止。

HTTP_ACCEPT HTTP字段头的特例。接受的值是:字段由逗号(,)分离。例如:如果下

面的几行是HTTP头的一部分:

接受:*/*,q=0.1

则URL(2.0新版本的特性)给出它的基础部分。

要注意的是,上面给出的信息片是由 TExtensionControlBlock 纪录自动传递的。因

此您不需要调用GetServerVariable。不过,如果您确有需要,特别是您要从ReadClient 中获得信息和需要知道要读入多少信息时,您可以调用它。

在很多时候,您不需要调用 ReadClient 。但是,您浏览器发出的信息量大于48KB

的时候,您需要调用 ReadClient 来获取其余的信息。

微软Build 2013开发者大会:盘点Windows 8.1的15个重大更新 瞧!这是摩托罗拉移动新Logo,你有什么感觉? 专访UEditor创始人战毅:打造最佳的用户编辑体验 Eclipse 4.3 正式版发布 代号Kepler 为兴趣而生 七大编程挑战项目,你敢来吗? iOS 7辅助功能:左右摇头控制iOS设备 挑战传统关系型数据库:Facebook图形数据库TAO揭秘 揭秘“棱镜计划”幕后的技术 你更新了吗?Visual Studio 2013 Preview和.NET 4.5.1同时发布 你主要使用什么编程语言? 跨平台游戏开发工具App Game Kit或将新增更多强悍功能 移动应用推广的那些事儿:应用发现篇 DDoS攻击也能成为一种云计算服务 Netflix开源Hadoop集群调度工具:日处理近万作业、上千TB数据 传谷歌正开发自己的视频游戏主机和智能手表 AMD APU战略转型:全民四核,发力“云+端” 富士康自主研发智能手表 惹苹果发来订单 开发者眼中的iOS 7:重设计过程没想象中那么糟 游戏引擎Unity宣布免费支持Windows Phone 8及Xbox One 蝉游记王益善专访:7人小团队实现App 30万下载量 去除IE 10广告的两种方法 直接拿来用!最火前端开源项目(二) 工作变得简单又高效 试试这八个WebApp 移动周报:生于微信,疯狂猜图的推广经 DataTorrent 将数据分析速度从“实时”提升至“现在时” 华为FusionCube开启全球规模商用 黑暗架构:以最少时间完成系统升级并获客户认可的开发之道 Hadoop Summit 2013见闻:创业与Hadoop 2.0不可阻挡 Facebook的清洁能源改革——革命尚未成功,仍需努力 YARN或将成为Hadoop新发力点 主流编程语言属性一览(多图) 谁能替我做一张图片啊? 气死人了,安装红帽子在输入根口令时遇到的尴尬:( 奇怪的oracle数据库错误? 公布我的软件了——网络邮盘,国内日下载量>1000,国外<10 关于堆和栈的队! 请问,准考证的英文怎么说? ASP终极开发:请成功用INSTALLSHIELD设置好IIS的高手朋友进来看看。100分全散了!!!!! 是屏保的问题?还是硬件故障? 紧急救命啊!各位大虾:)98关机时断电出现的故障 初级问题,请教大侠 在C#里面如何取得这个SQL値那? 请教, 扫描的问题... 不好意思,还有问题,关于span,恳请高手答完再下班。 心情郁闷,各位朋友安慰我一下好么。 如何注册JDBC驱动啊!我手头好多的驱动但是我不知道如何的注册这些驱动 在RICHEDIT里怎么实现换行输出? 200分求快速取中值的算法!(thirdapple) Flash Action 如何定时,就像Javascript中的setTimeout()? 保护的问题 用一个关于Printer对象的问题?各位大虾,初来乍到,请多多关照。 请问如何自动提交一个FORM? 请问各位师兄:web开发如何起步?例如使用JSP? 求助! 如何知道电脑中的网卡是什么型号的? 大家请帮忙,兄弟必重谢,分都给了 如何制作含有msdn数据库的安装程序 Web Service服务程序如何增加对SSL的支持? 怎么還可以上網﹐向各位大蝦求救 请问如何自动提交一个FORM? 如何处理并发访问! 如何在网页中打开*.ocx组件?是否能打开? 如果给我再来一次的机会...我还是想说... 求随机数发生器的算法 高手来拿高分!!!!!!!!!求开发自定义服务器控件的好书或代码 那位有ESQL(嵌入SQL)中文资料? 如何select LONG类型的字段? 红帽子里面可以演戏吗? 如何改变 Win me 快捷方式上的小键头图标。 ANY元素不是可以表示“任意”吗?为什么这个不行? 皇帝的新装 求助! 如何知道电脑中的网卡是什么型号的? 在winxp下如何安装winme实现双系统启动 在windows form下,怎样为datagrid添加click事件? 大家说小系统用什么数据库好,别说用ASA,ACCESS 大家好。我是凶宝宝。^_^请多关照!!! 关于date类型的比较 谁有电梯调度程序的源程序? 1000分 关于字符串的问题,请教!!! 那位大侠帮帮我,在VC++如何通过ODBC连接到oracle数据库? 请斑竹给出答案,闲人莫入,谢谢 问一个用法 如何编辑Windows系统中的右键属性菜单(高分) I think we must make it a rule to do some cleaning every day这句话是啥意思?请赐教!在下感激不尽 do some cleaning什么意思 is lost与has lost的区别,如:---Mum,I found my book_______.---Oh,you can get another one,But you must be careful next time.A.lose B.lost C.has lost D.is lost为什么选D?has lost 与is lost有何区别? 英语翻译 do some cleaning造句 语文课堂5分钟展示小故事等,要有哲理的老师把课堂每天留出5分钟,要我们轮班讲故事,能阐述哲理的,或是词语辨析等能丰富知识的,我想要一点出奇制胜的质料,简洁,易懂,有趣,有 Must I do _____ cleaning this Sunday?用some还是any 用do some cleaning写一篇60字左右的作文 圣诞的英文要怎么写 任务性阅读 木加冉念什么谢谢了,大神帮忙啊 求造句【虚拟语气】用虚拟语气造句~【过去】【现在】【将来】都要【各五句】..共15句,内容积极向上即可 英语作文过年准备 提手旁+冉念什么? 虚拟语气造句!用虚拟语气早四个不同的简单句 红花衣和日记本读后感初一读后感红花衣和日记本,或者读者2014年一月的两期任意一篇文章的读后感600字 任务性阅读 用虚拟语气造句(与现在事实相反)motorists speeding on highways ,more traffic accidents reportedif........ 急求《红花衣和日记本》阅读答案 任务性阅读, L've been to shanghai twice(对TWICE提问) ( )( ) ( )have you been to shanghai? I did some cleaning yesterday evening? 请多给我一些关于初一下册的首字母填空和任务性阅读 he has from the shanghai last year哪里错了 I DID SOME CLEANING YESTERDAY EVENING的问句 炎热的炎右边加个耳刀读什么 英语高级句式 I did some sports yesterday.改为否定句 上气下炎这个字怎么读是科学里N的那个字..上面是个气下面是个炎``给我拼音.. 英语高级句式求助!两年前我的家乡遭遇洪水灾害,居民财产被洪水吞没.用高级句式如何翻译? “英语格言”和“英语幽默”的英文怎么写? 炎黄的“炎”右边加一个“耳朵”是什么字啊.读什么音? 英语翻译 耳朵旁加一个冉念什么. 左边一个“炎”右边一个耳刀旁读什么? “升”这个字怎么读,急急急!谢谢是 这 个 字 “ 升” , 英语翻译Having finished her homework,Ma Li wants some music for relaxation.As usual,she starts her computer and goes to Baidu.com to download music files.But this time she is surprised when an announcement about protecting songs' copyright bursts 左耳右冉念什么 here are the notebook and the report that i promised you last week请问其中为什么要用are the,该句不是有一个last week,不是应该用过去式吗.请再帮我翻译一下 英语翻译就就就就就就就就就就 耳贵 和起来是什么字? Here ( ) ring and necklace that I promised you last week.A.is the B.are the C.was the D.has been a 中译英,请不要用google中译英:我们以前从不默音标,但是她要求我们默写单词要默音标.我们一开始觉得很难,但是后来,特别是在进了高中后,我们才知道她是为我们打好基础 英语广告英语的特点及其翻译 I'd like to return this watch that I bought here last week.为什么要用that?可不可以不加that,或者改成what 英语翻译Keeping onels head low can be a way of avoiding trouble ,but some types of trouble may necessitate heading in the opposite direction.十万火急!应该是Keeping one's head low can be a way of avoiding trouble ,but some types of trouble 耳字进门是什么字 Last week you lost my dictionary and l was so angry that l argued with you.是啥意思? 英语翻译韦政通说:“在所有的爱中,最强烈,最令人困惑,也是最缺乏稳定性的就是性爱.”(韦政通.中国的智慧[M].长春:吉林文史出版社,1988.250.)所以建立在性爱基础上的婚姻总是包含着“ Taken good care of by his family ,the old man lived ------- three years after the operation .A.other B.more C.another D.every 我选的是C.但是错了,可以帮我分析一下原因吗?顺便答案是什么呢 permit sb.to do 是这个用法吗 翻译英文"Love story" He ( ) to the hospital after the accident. A. took B. was taking C. was takenHe ( ) to the hospital after the accident.A. tookB. was takingC. was taken这答案是什么 missing 与lost区别 love story翻译 被照顾 是be looked after ,还是be taken care of lost和missing之间的区别是什么啊? love story的翻译 苯女孩别太傻得英文怎么拼?如题 lost和missing的区别有的时候做题目,会要在lost和missing之中选择一个选项的,请问这个时候会有什么区别呢谢谢啊,那么有the missing boy的,有没有the lost boy呢?
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘