📄 delphi&internet1.txt
字号:
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页上增添这个超链接:
<A HREF="/scripts/mystuff/isapi1.dll" >ISAPI One</A><BR>
当用户点击这个超链接时,ISAPI1 Dll会被呼叫,然后字符串“Hello from ISAPI”会显示在用户的浏览器
上。如果您并不是把 ISAPI.DLL放在 mystuff 目录下,那么您应该修改上面的HTML代码来使之与您的情况
适应。注意,您的目录必须与目录 inetsrv 有关,不应,也不能包含您的整个DLL所在的目录。
下面是呼叫的完整的HTML脚本:
<HTML>
<HEAD>
<TITLE>CharlieC Home Page</TITLE>
</HEAD>
<BODY>
<H1>My Home Page </H1>
<P>
This is the home page for my home computer.
<P>
<A HREF="/scripts/mystuff/isapi1.dll" >ISAPI One</A><BR>
</BODY>
</HTML>
注意,如果您多次把程序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语言中的一种格式(译者)] :
<A HREF="/scripts/mystuff/test1.dll">Test One</A>
如果您希望响应这个DLL,您就要对上面那行做这样的改动:
<A HREF="/scripts/mystuff/test1.dll?MyQuery">Test One</A>
假如HTML代码段中有像上面两行中的第二行,那么,您的DLL就会在lpszQueryString参数中得到“MyQuery”
的字符串,特别要注意跟在请求字符串后的请求标志的使用。
当然,您可以随心所欲地改变请求字符串。例如,您可以这样写:
<A HREF="/scripts/mystuff/test1.dll?ServerName">Test One</A>
在这个请求中,这个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(
'<HTML><TITLE>Test server result</TITLE>' +
'<H1>Test server results</H1>' +
'Size = %d<BR>'+
'Version = %.8x<BR>'+
'ConnID = %.8x<BR>'+
'Method = %s<BR>' +
'Query = %s<BR>' +
'PathInfo = %s<BR>'+
'PathTranslated = %s<BR>'+
'TotalBytes = %d<BR>'+
'AvailableBytes = %d<BR>'+
'ContentType = %s<BR><BR>'+
'<H1>Some Server Variables</H1>',
[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+'<BR>';
StrLen := SizeOf(Buf);
GetServerVariable(ConnID, 'REMOTE_HOST', @Buf, StrLen);
ResStr := ResStr + 'Remote_Host = '+Buf+'<BR>';
StrLen := SizeOf(Buf);
GetServerVariable(ConnID, 'REMOTE_USER', @Buf, StrLen);
ResStr := ResStr + 'Remote_User = '+Buf+'<BR>';
StrLen := SizeOf(Buf);
GetServerVariable(ConnID, 'SERVER_NAME', @Buf, StrLen);
ResStr := ResStr + 'SERVER_NAME = '+Buf+'<BR>';
StrLen := SizeOf(Buf);
GetServerVariable(ConnID, 'SERVER_PORT', @Buf, StrLen);
ResStr := ResStr + 'SERVER_PORT = '+Buf+'<BR>';
StrLen := SizeOf(Buf);
GetServerVariable(ConnID, 'SERVER_PROTOCOL', @Buf, StrLen);
ResStr := ResStr + 'SERVER_PROTOCOL = '+Buf+'<BR>';
StrLen := SizeOf(Buf);
GetServerVariable(ConnID, 'SERVER_SOFTWARE', @Buf, StrLen);
ResStr := Format('%sSERVER_SOFTWARE = %s<BR>'+
'ThreadID = %.8x<BR>',[ResStr, Buf, GetCurrentThreadID]);
end;
ResStr := ResStr + '</HTML>';
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,您应该建立一个包括下面这行的 HRML 脚本
<A HREF="/scripts/mystuff/test1.dll">Test One</A> <BR>
从“确认”按钮获得信息
通常向您发送信息的HTML窗体中都有一个确认按钮。只要信息量小于49KB,您就可以认为TExetensionControlBlock中的 lpbData 字段是可用的。这里显示了您可以如何在大多数情况下获得由这个字段的指针发来的信息:
var
S: string;
begin
…
S := PChar(ECB.lpbData);
…
end;
如果从这个字段传来的信息大于48KB,那么您必须呼叫 ReadClient 来获得其余的信息。
如果您想要确切地知道在 lpbData 字段中哪些信息是可用的,您可以使用下面两个函数把数据传回到您的网页浏览器中:
function SetUpResString: string;
begin
Result := '<HTML>' +
'<TITLE>Test server result</TITLE>' +
'<H1>Test server results</H1>' +
'<BODY>lpbData = %s </BODY>' +
'</HTML>';
end;
function HttpExtensionProc(var ECB: TExtensionControlBlock):
DWORD; stdcall;
var
ResStr: string;
StrLen: Integer;
S, S1: string;
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -