轉貼
用MODEM撥接,仍是大多數個人網民選擇上網的方式.如果
能在我們的應用程式中管理撥號
網路(如Foxmail、Sygate中的撥號功能),無疑將會方便我們的軟
件使用者(不用再切換應用程式,
運行撥號網路),提高我們的軟體的友好性從而提高軟體的競爭
力.
在WIN9X下,如果安裝了撥號網路,則在WINDOWS系統的系統目
錄System下將有兩個撥號網路管理
程式庫RasApi32.DLL和RasApi16.DLL,我們可利用其中的函數來獲
取和設定撥號連線網路的資訊。在
Delphi協助檔案中,有相關函數的說明。
在此,我們要討論的管理項目有:
1、擷取當前系統中可用的撥號連線名稱
2、建立撥號連線、修改撥號連線的屬性
3、擷取和設定撥號連線的撥號參數
4、用指定的撥號連線撥號、掛斷指定的撥號連線
5、擷取當前活動的串連及其串連狀態
零、擷取RasAPI函數執行失敗的錯誤資訊
RasAPI的調用介面是統一的,但對於不同的Windows版本,許多
常量和資料結構的定義是不同的。
如果使用的資料結構與Windows版本不對應,RasAPI函數執行會失
敗;另外,其它原因如其它程式也
在使用同一個撥號連線進行撥號等,也會造成RasAPI函數執行失
敗.鑒於此原因,我們需要先討論獲
取RasAPI函數執行失敗的錯誤資訊的函數。
當一個RasAPI函數執行結束時,會返回一個結果標識,為0時表
示執行成功,否則作為一個錯誤標
識符表示執行失敗的原因。RasAPI函數RasGetErrorString可以根據
錯誤標識符返回其錯誤描述資訊,
在中文WIN9X下可提供一個中文錯誤資訊.
RasGetErrorString的函數原型為:
function RasGetErrorString (
uErrorValue : UINT;//錯誤標識符
lpszErrorString : PChar;//錯誤提示資訊的緩衝區
cBufSize : DWORD//錯誤提示資訊的緩衝區大小
) : DWORD; stdcall;
function RasGetErrorString;external RasApiDll name
'RasGetErrorStringA';
( 註:RasApiDll = 'Rasapi32.dll'; )
為了方便,我們可以自己編寫一個函數,用於擷取RasAPI函數
執行失敗的錯誤資訊。在下面的例
子中,會經常用到該函數。函數代碼如下所示:
{ 根據錯誤標識符,擷取RasAPI函數執行失敗的錯誤資訊 }
function GetRasError( ErrorID : UINT ) : string;
var
buffer : array[ 0..255 ] of char;
begin
if 0 = RasGetErrorString( ErrorID, buffer, 256 ) then
result := strpas( buffer )//如果能正確返回錯誤資訊,則轉化
為Pascal字串
else//否則返回16進位形式的錯誤碼
result := 'GetRasError Failure:ErrorID=' + Format(
'%x',[ErrorID] );
end;
一、擷取當前系統中可用的撥號連線名稱
即擷取系統中已建立的撥號連線的名稱,可用來讓使用者選擇使
用哪個撥號連線進行撥號.
可以用兩種方法來實現.一種是利用RasAPI函數;另一種是不
用RasAPI函數,直接在註冊表中查詢.
1、不用RasAPI函數,直接在註冊表中查詢
在註冊表的
HKEY_USERS/.Default/RemoteAccess/Addresses下,列出了已經在
撥號網路中建立
了的撥號連線的名稱及其屬性設定,其中各項目的名稱即為可
用的撥號連線的名稱;各項目的值即
為各撥號連線的屬性設定,不過是二進位串,筆者還看不
懂.由此可見,我們只要讀出各項目的名
稱即可擷取當前系統中可用的撥號連線名稱.
var
registryTemp : TRegistry;
stringsTemp : TStringlist;
begin
registryTemp := TRegistry.Create;
stringsTemp := TStringlist.Create;
with registryTemp do
begin
RootKey := HKEY_USERS;//根鍵設定為HKEY_USERS
//如果存在子鍵.Default/RemoteAccess/Addresses
if OpenKey('.Default/RemoteAccess/Addresses',false) then
GetValueNames( stringsTemp );//讀出各項目的名稱,即撥號連線
名稱
end;
combobox1.Items.assign( stringsTemp );//顯示,供選擇
end;
2、用RasAPI函數
RasAPI函數RasEnumEntries可擷取當前系統中可用的撥號連線名
稱.其函數原型為
function RasEnumEntries(
reserved : PChar;//保留欄位,必須為NIL
lpszPhonebook : PChar;//電話本名稱,在Win9X下無作用,可為空白字
符串
lprasentryname : LPRASENTRYNAME;//接收撥號連線名稱的緩衝區,
是一個RASENTRYNAME類型數組的指標
var lpcb : DWORD;//接收撥號連線名稱的緩衝區的大小(Bytes)
var lpcEntries: DWORD//實際獲得的撥號連線名稱的數目
) : DWORD; stdcall;
function RasEnumEntries;external RasApiDll name
'RasEnumEntriesA';
參數lprasentryname提供了一個RASENTRYNAME類型數組的指標,指
向一個接收撥號連線名稱的緩衝
區,其中RASENTRYNAME及LPRASENTRYNAME的類型說明如下:
LPRASENTRYNAME = ^RASENTRYNAME;
RASENTRYNAME = record
dwSize : DWORD;//該結構所佔記憶體的大小(Bytes),一般設定為
SizeOf(RASENTRYNAME)
szEntryName : array [ 0..RAS_MaxEntryName ] of char;//撥號
串連名稱
end;
lpcb為緩衝區的大小,一般設定為dwSize的倍數,倍數為
可能有的串連的個數.
lpcEntries實際的串連的個數.
下面是一個應用例子,列出了當前系統中可用的撥號連線
名稱.
注意,應在RASENTRYNAME緩衝區的第一個RASENTRYNAME結構中設定
dwSize.
const
MaxPhoneEntries = 10;//最多的撥號連線數目
var
intIndex : integer;
PhoneEntries : array[ 0..MaxPhoneEntries - 1 ] of
RASEntryName;
dwSize, dwEntries, dwResult : DWORD;
begin
//在RASENTRYNAME緩衝區的第一個RASENTRYNAME結構中設定dwSize
PhoneEntries[ 0 ].dwSize := sizeof( RASEntryName );
dwSize := MaxPhoneEntries * sizeof( RASEntryName );//為緩衝
區的大小
//調用RasAPI函數,擷取當前系統中可用的撥號連線名稱
dwResult := RasEnumEntries ( NIL,'',@PhoneEntries[ 0
],dwSize, dwEntries );
if dwResult <> 0 then
begin//RasAPI函數,執行錯誤
memo1.lines.add('RasEnumEntries錯誤:' + GetRasError(
dwResult ));
exit;
end;
//顯示當前系統中可用的撥號連線名稱
memo1.lines.add('共有' + inttostr( dwEntries ) + '個RAS連
接,如下所示');
for intIndex := 0 to dwEntries -1 do
memo1.lines.add( strpas( PhoneEntries[ intIndex
].szEntryName ) );
end;
3、擷取預設的撥號連線的名稱
預設的撥號連線,即使用者在瀏覽器中設定的撥號連線,該
串連可以認為是使用者最常用
的撥號連線.
在註冊表的HKEY_USERS/.Default/RemoteAccess位置,有
一個字串名Profile,它對應
字元值即為HKEY_USERS/.Default/RemoteAccess/Addresses.
二、建立撥號連線、修改撥號連線的屬性
RasAPI函數RasCreatePhonebookEntry、
RasEditPhonebookEntry通過調用Win9X的
建立撥號連線、修改撥號連線的設定介面,允許使用者建立撥號連
接、修改撥號連線,具體
的設定作業還要由系統來完成.
1、建立撥號連線
建立撥號連線的RasAPI函數為RasCreatePhonebookEntry,
其函數原型為:
function RasCreatePhonebookEntry(
hwnd : THandle; //建立撥號連線視窗的父視窗的控制代碼,為NIL表示
案頭(DeskTop)
lpszPhonebook: pchar//電話本名稱,在Win9X下無作用,可為空白字元
串
) : DWORD;stdcall;
function RasCreatePhonebookEntry;external RasApiDll name
'RasCreatePhonebookEntryA';
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,允許使用者建立一個撥號連線.
var
dwResult : DWORD;
begin
//在當前視窗中建立撥號連線
dwResult := RasCreatePhonebookEntry( handle, '' );
if dwResult = 0 then
memo1.lines.add('建立撥號連線成功!')
else
memo1.lines.add('建立撥號連線失敗:!' + GetRasError(
dwResult ))
end;
2、修改撥號連線的屬性
修改撥號連線的屬性的RasAPI函數為
RasEditPhonebookEntry,其函數原型為:
function RasEditPhonebookEntry(
hwnd : THandle; //建立撥號連線視窗的父視窗的控制代碼,為NIL表示
案頭(DeskTop)
lpszPhonebook: pchar;//電話本名稱,在Win9X下無作用,可為空白字
符串
lpszEntryName: pchar//撥號連線的名稱,如'163'、'169'等
) : DWORD; stdcall;
function RasEditPhonebookEntry;external RasApiDll name
'RasEditPhonebookEntryA';
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,允許使用者修改指定撥號連線的屬
性.
var
dwResult : DWORD;
begin
//在當前視窗中修改撥號連線的屬性
dwResult := RasEditPhonebookEntry( handle, '', '163' );
if dwResult = 0 then
memo1.lines.add('修改撥號連線成功!')
else
memo1.lines.add('修改撥號連線失敗:!' + GetRasError(
dwResult ))
end;
三、擷取和設定撥號連線的撥號參數
用RasAPI函數RasGetEntryDialParams、
RasSetEntryDialParams可以直接擷取和設定指定撥號
串連的撥號參數,其中包括使用者名稱稱和使用者密碼!
1、擷取撥號連線的撥號參數
擷取撥號連線的撥號參數RasAPI函數為
RasGetEntryDialParams,其函數原型為:
function RasGetEntryDialParams(
lpszPhonebook: pchar;//電話本名稱,在Win9X下無作用,可為空白字
符串
pRASDIALPARAMS:LPRASDIALPARAMS;//撥號參數,是一個
RASDIALPARAMS類型的指標
var lpfPassword : WordBool//是否需要使用者密碼
) : DWORD; stdcall;
function RasGetEntryDialParams;external RasApiDll name
'RasGetEntryDialParamsA';
參數pRASDIALPARAMS是一個RASDIALPARAMS類型的指標,指向一個撥
號串連的撥號參數資料
的緩衝區,其中RASDIALPARAMS及LPRASDIALPARAMS的類型說明如
下:
LPRASDIALPARAMS = ^RASDIALPARAMS;
RASDIALPARAMS = record
dwSize : DWORD;//該結構所佔記憶體的大小(Bytes),一般設定為
SizeOf(RASDIALPARAMS)
szEntryName : array[0..RAS_MaxEntryName] of char;//撥號連線
名稱
szPhoneNumber : array[0..RAS_MaxPhoneNumber] of char;//撥號
號碼
szCallbackNumber : array[0..RAS_MaxCallbackNumber] of
char;//回叫號碼
szUserName : array[0..UNLEN] of char;//使用者名稱稱
szPassword : array[0..PWLEN] of char;//使用者密碼
szDomain : array[0..DNLEN] of char;//網域名稱
end;
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,擷取指定撥號連線的撥號參數.
var
dwResult : DWORD;
RASDIALPARAMSData : RASDIALPARAMS;
NeedPWD : WordBool;
begin
//指定撥號連線的名稱
with RASDIALPARAMSData do
begin
dwSize := sizeof( RASDIALPARAMS );//結構大小
szEntryName := '163';//指定撥號連線的名稱
szUserName := '';//其它五個參數初始化
szPassword := '';
szDomain := '';
szCallbackNumber := '';
szPhoneNumber := '';
end;
NeedPWD := true;//需要使用者密碼
//擷取指定撥號連線的撥號參數
dwResult := RasGetEntryDialParams(
'',@RASDIALPARAMSData,NeedPWD );
if dwResult <> 0 then //擷取指定撥號連線的撥號參數失敗
memo1.lines.add( '擷取
'+StrPAS(RASDIALPARAMSData.szEntryName )+'撥號參數失敗:'
+ GetRasError( dwResult ))
else
begin//顯示指定撥號連線的撥號參數
memo1.lines.add( StrPAS(RASDIALPARAMSData.szEntryName )+'撥
號參數如下');
memo1.lines.add( '使用者名稱稱:' +
StrPAS(RASDIALPARAMSData.szUserName ));
memo1.lines.add( '使用者密碼:' +
StrPAS(RASDIALPARAMSData.szPassword ));
memo1.lines.add( '域 名:' +
StrPAS(RASDIALPARAMSData.szDomain ));
memo1.lines.add( '回叫號碼:' +
StrPAS(RASDIALPARAMSData.szCallbackNumber ));
memo1.lines.add( '撥號號碼:' +
StrPAS(RASDIALPARAMSData.szPhoneNumber ));
end;
end;
2、設定撥號連線的撥號參數
設定撥號連線的撥號參數RasAPI函數為
RasSetEntryDialParams,其函數原型為:
function RasSetEntryDialParams(
lpszPhonebook: pchar;//電話本名稱,在Win9X下無作用,可為空白字
符串
pRASDIALPARAMS:LPRASDIALPARAMS;//撥號參數,是一個
RASDIALPARAMS類型的指標
var lpfPassword : WordBool//是否刪除使用者密碼
) : DWORD; stdcall;
function RasSetEntryDialParams;external RasApiDll name
'RasSetEntryDialParamsA';
參數pRASDIALPARAMS的說明同RasGetEntryDialParams.
參數lpfPassword的含義與RasGetEntryDialParams不同,在此表示
是否刪除使用者密碼,為
TRUE時將原來的使用者密碼刪除.
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,設定指定撥號連線的撥號參數.
var
dwResult : DWORD;
RASDIALPARAMSData : RASDIALPARAMS;
RemovePWD : WordBool;
begin
//指定撥號連線的撥號參數
with RASDIALPARAMSData do
begin
dwSize := sizeof( RASDIALPARAMS );//結構大小
szEntryName := '163';//指定撥號連線的名稱
szUserName := 'MyName';//指定使用者名稱稱
szPassword := 'MyPassword';//指定使用者密碼
szDomain := 'MyDomain';//指定網域名稱
szCallbackNumber := '';//指定回叫號碼
szPhoneNumber := '';//指定撥號號碼
end;
RemovePWD := false;//不需要刪除使用者密碼
//設定指定撥號連線的撥號參數
dwResult := RasSetEntryDialParams(
'',@RASDIALPARAMSData,RemovePWD );
if dwResult <> 0 then //設定指定撥號連線的撥號參數失敗
memo1.lines.add( '設定
'+StrPAS(RASDIALPARAMSData.szEntryName )+'撥號參數失敗:'
+ GetRasError( dwResult ))
else
begin//顯示指定撥號連線的撥號參數
memo1.lines.add( StrPAS(RASDIALPARAMSData.szEntryName )+'撥
號參數設定成功');
memo1.lines.add( '使用者名稱稱:' +
StrPAS(RASDIALPARAMSData.szUserName ));
memo1.lines.add( '使用者密碼:' +
StrPAS(RASDIALPARAMSData.szPassword ));
memo1.lines.add( '域 名:' +
StrPAS(RASDIALPARAMSData.szDomain ));
memo1.lines.add( '回叫號碼:' +
StrPAS(RASDIALPARAMSData.szCallbackNumber ));
memo1.lines.add( '撥號號碼:' +
StrPAS(RASDIALPARAMSData.szPhoneNumber ));
end;
end;
四、用指定的撥號連線撥號、掛斷指定的撥號連線
1、用指定的撥號連線撥號
撥號有兩種方法,一種是調用撥號網路中的撥號程式,就
象使用者自己用滑鼠雙擊撥號連線名稱一
樣,使用者可以修改撥號號碼、使用者名稱稱和使用者密碼,最後由撥
號網路來完成撥號過程;另一個方法則
是調用RasAPI函數.
(1)用撥號網路中的撥號程式
在程式中可以用如下代碼:
winexec('rundll32.exe rnaui.dll,RnaDial 169',
SW_SHOWNORMAL);
其中字串中的最後一個參數“169”為撥號連線的
名稱。
(2)用RasAPI函數撥號
用撥號連線撥號的RasAPI函數為RasDial,其函數原型為:
function RasDial(
pRasDialExtensions : LPRASDIALEXTENSIONS;//在WIN9X下無用,可
設定為NIL
lpszPhonebook : PChar;//電話本名稱,在Win9X下無作用,可為空白
字串
lpRasDialParams : LPRASDIALPARAMS;//撥號參數
dwNotifierType : DWORD;//訊息通知方式
handle : TRasDialFunc;//訊息處理事件
var lphRasConn : DWORD//返回的成功串連的串連控制代碼
) : DWORD; stdcall;
function RasDial;external RasApiDll name 'RasDialA';
參數pRASDIALPARAMS的說明同RasGetEntryDialParams.
參數dwNotifierType表示訊息通知方式.在撥號過程中,
系統發出撥號事件訊息可以通知撥號
進度,因而需要提供接受訊息的方式和處理訊息的函數.當其
值為$FFFFFFFF時,則參數handle被
視為一個視窗的控制代碼,事件訊息被發生該視窗處理;當其值為0
時,handle被視為TRasDialFunc類型
的函數的指標;當其值為1時,handle被視為TRasDialFunc1類
型的函數的指標.
參數handle表示訊息處理函數指標,其類型可以THandle、
TRasDialFunc、TRasDialFunc1.當該
參數不為NULL或NIL時,其代表的訊息處理函數指標將接受撥號
進度通知訊息;為NIL時,撥號過程由
非同步方式變為同步方式,直到撥號過程成功或失敗後RasDial函
數才返回.詳細可參見Delphi協助檔案
中關於RasDial函數的協助。
當撥號連線成功時,lphRasConn將表示其串連控制代碼.
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,按指定的撥號參數撥號.
var
RASDIALPARAMSData : RASDIALPARAMS;
dwResult : DWord;
RasCon : DWord;
begin
//指定撥號連線的撥號參數
with RASDIALPARAMSData do
begin
dwSize := sizeof( RASDIALPARAMS );//結構大小
szEntryName := '163';//指定撥號連線的名稱
szUserName := 'MyName';//指定使用者名稱稱
szPassword := 'MyPassword';//指定使用者密碼
szDomain := 'MyDomain';//指定網域名稱
szCallbackNumber := '';//指定回叫號碼
szPhoneNumber := '';//指定撥號號碼
end;
//用指定的撥號參數撥號,採用同步撥號方式
dwResult := RasDial( NIL,'',@RASDIALPARAMSData,0,NIL,RasCon
);
if dwResult <> 0 then //設定指定撥號連線的撥號參數失敗
memo1.lines.add( '用'+StrPAS(RASDIALPARAMSData.szEntryName
)+'撥號失敗:'
+ GetRasError( dwResult ))
else
memo1.lines.add( '用'+StrPAS(RASDIALPARAMSData.szEntryName
)+'撥號成功!' );
end;
2、掛斷指定的撥號連線
掛斷撥號連線的RasAPI函數為RasHangUp,其函數原型為:
function RasHangUp(
hRasConn : DWORD//要掛斷的撥號連線的控制代碼
) : DWORD; stdcall;
function RasHangUp;external RasApiDll name 'RasHangUpA';
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,掛斷由RasDial建立的撥號連線.
//掛斷由RasDial建立的撥號連線
dwResult := RasHangUp( RasCon );
if dwResult <> 0 then //掛斷失敗
memo1.lines.add( '掛斷失敗:' + GetRasError( dwResult ))
else
memo1.lines.add( '掛斷成功!');
五、擷取當前活動的串連及其串連狀態
1、擷取當前活動的串連
擷取當前活動的串連的RasAPI函數為
RasEnumConnections,其函數原型為:
function RasEnumConnections( var lprasconn : RASCONN ;//接
收活動串連的緩衝區的指標
var lpcb: DWORD;//緩衝區大小
var lpcConnections : DWORD//實際的活動串連數
) : DWORD; stdcall;
function RasEnumConnections;external RasApiDll name
'RasEnumConnectionsA';
參數lprasconn提供了一個RASCONN類型數組的指標,指向一個接收
活動串連的緩衝
區,其中RASCONN的類型說明如下:
RASCONN = record
dwSize : DWORD;//該結構所佔記憶體的大小(Bytes),一般設定為
SizeOf(RASCONN)
hrasconn : HRASCONN;//活動串連的控制代碼
szEntryName : array[0..RAS_MaxEntryName] of char;//活動串連
的名稱
szDeviceType : array[0..RAS_MaxDeviceType] of char;//活動連
接的所用的裝置類型
szDeviceName : array[0..RAS_MaxDeviceName] of char;//活動連
接的所用的裝置名稱
end;
參數lpcb為緩衝區大小(Bytes).
參數lpcConnections將返回實際的串連數目.
函數傳回值為0表示執行成功;否則為錯誤碼.
2、擷取指定串連的串連狀態
擷取指定串連的串連狀態的RasAPI函數為
RasGetConnectStatus,其函數原型為:
function RasGetConnectStatus(
hrasconn : HRASCONN; //指定活動串連的控制代碼
lprasconnstatus : LPRASCONNSTATUS//串連狀態參數
) : DWORD; stdcall;
function RasGetConnectStatus;external RasApiDll name
'RasGetConnectStatusA';
串連狀態參數lprasconnstatus是一個RASCONNSTATUS類型
的指標,將返回串連狀態參數.
RASCONNSTATUS和LPRASCONNSTATUS的類型說明如下:
LPRASCONNSTATUS = ^RASCONNSTATUS;
RASCONNSTATUS = record
dwSize : DWORD;//該結構所佔記憶體的大小(Bytes),一般設定為
SizeOf(RASCONNSTATUS)
rasconnstate : RASCONNSTATE;//串連狀態標識,一組DWORD類型數
值的集合。
dwError : DWORD;//錯誤類型標識符
szDeviceType : array[0..RAS_MaxDeviceType] of char;//活動連
接的所用的裝置類型
szDeviceName : array[0..RAS_MaxDeviceName] of char;//活動連
接的所用的裝置名稱
end;
函數傳回值為0表示執行成功;否則為錯誤碼.
下面是一個應用例子,列出了當前系統中活動的串連的名
稱及其串連狀態.
注意,應在RASCONN緩衝區的第一個RASCONN結構中設定dwSize.
const
MaxConnections = 10;//最多的撥號連線數目
var
connections : array[0..MaxConnections-1] of RASCONN;
longSize : dword;
intAvailabelConnections : dword;
intIndex : integer;
dwResult : DWORD;
strTemp : string;
RASCONNSTATUSData : RASCONNSTATUS;
begin
connections[ 0 ].dwSize := sizeof(RASCONN);//結構的大小
longSize := MaxConnections * connections[ 0 ].dwSize;//緩衝
區大小
intAvailabelConnections := 0;//實際的活動串連的數目
//擷取當前系統中活動的串連
dwResult := RasEnumConnections( connections[ 0 ], longSize,
intAvailabelConnections );
if dwResult <> 0 then //擷取當前系統中活動的串連
memo1.lines.add( '擷取當前系統中活動的串連:' +
GetRasError( dwResult ))
else
begin
memo1.lines.add( '當前系統中活動的串連' + inttostr(
intAvailabelConnections )
+ '個,列舉如下' );
for intIndex := 0 to intAvailabelConnections - 1 do
begin
strTemp := '串連名稱:' + StrPAS( connections[ intIndex
].szEntryName )
+ ' 裝置類型:' + StrPAS( connections[ intIndex
].szDeviceType )
+ ' 裝置名稱:' + StrPAS( connections[ intIndex
].szDeviceName );
//擷取串連狀態
dwResult := RasGetConnectStatus( connections[ intIndex
].hRasConn,@RASCONNSTATUSData );
if 0 <> dwResult then
strTemp := strTemp + ' 串連狀態未知:' + GetRasError(
dwResult )
else if RASCONNSTATUSData.rasconnstate = RASCS_Connected
then
strTemp := strTemp + ' 串連狀態:已串連'
else
strTemp := strTemp + ' 串連狀態:(' +
inttostr(RASCONNSTATUSData.rasconnstate)+')';
memo1.lines.add( strTemp );
end;
end;
end;
以上程式在PWIN98+Delphi3.0下調試通過。