這篇文章主要講述如何讓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過濾文字做下面這些事情:
使用者授權方案
壓縮
加密
登入
通訊分析或其他請求分析(例如,尋找"....etcpassWord" 中的請求)
在本文中,我會著重介紹如何編寫傳回資料集的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 := ' +
'
'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:winntsystem32inetsrvscriptsmystuffisapi1.dll
在這個例子中,我已經建立了我的名為「mystuff」的目錄
它只不過是用來儲存我創建的
ISAPI DLLs。您的目錄,當然和我的機器上的不完全一樣,取決於您的“inetsrv”目錄位置和其它因素。
為成功呼叫這個DLL,您應該在您的HTML頁面上增添這個超連結:
ISAPI One
當使用者點擊這個超連結時,ISAPI1 Dll會被呼叫,然後字串「Hello from ISAPI」會顯
示在使用者的瀏覽器上。如果您並不是把ISAPI.DLL放在mystuff 目錄下,那麼您應該修改上面的HTML程式碼來使其與您的情況適應。注意,您的目錄必須與目錄inetsrv 有關,不應,也不能包含您的整個DLL所在的目錄。
下面是呼叫的完整的HTML腳本:
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(
' +
'
'Size = %d
'+
'Version = %.8x
'+
'ConnID = %.8x
'+
'Method = %s
' +
'Query = %s
' +
'PathInfo = %s
'+
'PathTranslated = %s
'+
'TotalBytes = %d
'+
'AvailableBytes = %d
'+
'ContentType = %s
'+
'
[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 := ' +
' +
'
'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窗體:
這段程式碼會產生一個包含一個供您輸入數字的文字區和一個叫做「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 := ' +
' +
'
'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:
}
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 := ' +
' +
'
'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 來取得其餘的資訊。