1.1 關於Delphi的BDE
Delphi操作數據庫主要是利用BDE來進行。BDE是基於32位Windows內核的數據庫引擎和連接工具,支持現有的大多數數據庫。它具有如下一些特點:
1. 為多種數據庫格式提供統一的應用程序接口,包括任何ODBC數據源。
2. 適應C/S數據庫應用的開發,程序設計人員可以訪問所有本地和服務端的數據,並且很容易實現應用的向上兼容性。
3. 對於Delphi標准的數據庫訪問(如Paradox和dBASE),BDE提供的連接方式是最快的。
4. 直接實時的訪問數據源。
1.1.1 如何自定義BDE的驅動程序
BDE中按照各個文件的作用可分為以下幾部分,這些文件均保存在“…/Program Files/Common Files/Borland Shared/BDE”目錄下。
1. 必備文件
Blw32.dll:語言驅動函數庫。
Idapi32.dll:BDE基本函數庫。
Fareast.bll、Usa.bll:遠東語言及美國語言驅動程序,對中文軟件不可缺少。
Idr20009.dll:錯誤信息庫。
2. 數據庫驅動程序(可選)
Idasci32.dll:Ascii文本數據庫驅動程序函數庫;
Iddao32.dll:Access數據庫驅動程序函數庫;
Iddbas32.dll:dBase數據庫驅動程序函數庫;
Idodbc32.dll:ODBC數據庫驅動程序函數庫;
Idpdx32.dll:Paradox數據庫驅動程序函數庫。
3. 其他驅動程序和配置文件(可選)
Idbat32.dll:批操作驅動程序函數庫,如果不用TBatchMove組件或DbiBatchMove類函數,可以不要。
Iddr32.dll:Data Repository驅動程序函數庫,如果不用Data Repository功能,可以不要。
Idprov32.dll:BDE DataSet provide驅動程序函數庫,如果不用TProvider組件,可以不要。
Idqbe32.dll:QBE驅動程序函數庫,如果不用Query By Example,可以不要。
Idsql32.dll:SQL查詢驅動程序函數庫,如果不用TQuery進行查詢,可以不要。
Idapi32.cfg:BDE配置文件,如果程序中沒有特殊要求,可以不要。也可以在BDE管理器中設置正確后再分發。
4. 其他文件(一般不用)
BDE32.HLP、BDE32.CNT:BDE幫助文件。
BdeAdmin.exe、BdeAdmin.HLP、BdeAdmin.CNT:BDE管理器及幫助文件。
*.BLL:其他國家和地區的語言驅動程序。
DataBump.EXE、DataBump.HLP:數據庫數據轉移工具及幫助文件。
Localsql.HLP、Localsql.CNT:SQL查詢語句幫助文件。
Sqllnk32.HLP、Sqllnk32.CNT:SQL連接幫助文件。
1.1.2 如何注冊BDE
安裝BDE僅僅復制前面提到的各種文件是不行的,還要等到向注冊表注冊之后才可以使用。
必需的注冊表項目包括:
1. BDE動態鏈接庫文件位置設置(如圖1所示)
Key:HKEY_LOCAL_MACHINE/Software/Borland/Database Engine
Item:DLLPATH
Value:BDE動態鏈接庫文件所在位置
圖1 BDE動態鏈接庫文件位置設置
2. BDE語言驅動文件路徑設置(如圖2所示)
Key:HKEY_LOCAL_MACHINE/Software/Borland/BLW32
Item:BLAPIPATH
Value:BDE語言驅動文件所在路徑
圖2 BDE語言驅動文件路徑設置
3. 定義可用的BDE語言驅動文件(如圖3所示)
Key:HKEY_LOCAL_MACHINE/Software/Borland/BLW32
Item:LOCALE_LIB#" (#表示數字, 如"LOCALE_LIB1"、"LOCALE_LIB3"等)
Value:指定各BDE語言驅動文件
圖3定義可用的BDE語言驅動文件
1.1.3 如何在運行期檢測和建立數據庫別名
在前面學習的案例中,數據庫別名的建立、修改和刪除等維護工作,一般在BDE Administrator中進行,並在設計期就已經設置完畢,但這些功能在運行期將如何實現呢?在Delphi的BDE中,當數據庫類型為STANDARD時,其別名定義最為簡單,但這時僅能使用Paradox,dBASE,ASCIIDRV三種數據庫作為默認的驅動程序。另外還需定義數據庫存儲路徑(PATH)和ENABLE BCD,這樣才能建立一個完整的數據庫別名。
Delphi的數據庫應用程序能自動提供一個Session組件,這個Session組件即為應用程序與BDE的接口。
1. 檢測別名
通過調用Session.GetAliasNames(list:Tstrings)方法,可將當前BDE配置中的所有數據庫別名的名稱存放到List字符串列表中。然后調用list.IndexOf(需要檢測的別名')函數,該函數的返回值可用來檢測數據庫別名是否存在(若其值為-1則不存在)。
2. 添加別名
使用Session組件的過程AddStandardAlias(constName,Path,DefaultDriver:string),可以添加一個標准類型的數據庫別名。如添加一個數據庫別名為NewAlias,其默認數據庫驅動程序為Paradox、存儲路徑為c:/instance1,則可以使用下面的語句。
Session.AddStandardAlias('NewAlias',' c:/instance1','Paradox');
3. BDE配置文件存盤
使用Session.SaveConfigFile語句可將設置信息保存。
一般可以在窗體的OnCreate事件中添加檢測和創建數據庫別名的程序代碼,下面就是一個在運行期實現上述功能的例子。首先單擊Delphi工具欄上的“New Form”按鈕,新建一窗體,在該窗體上添加一個Table組件,用以提供TSession對象。然后在窗體的OnCreate事件中添加如下所示的代碼,程序執行時,系統會首先檢測NewAlias別名是否存在,若不存在,提示用戶是否創建這個別名,如圖4所示。
Procedure TForm1.FormCreate(Sender: TObject);
var
sl:TStringList; //字符串列表變量
re:Integer;
begin
sl:=TStringlist.Create;
Session.GetAliasNames(sl); //取得別名列表
if (sl.IndexOf('NewAlias')=-1) then //判斷別名是否存在
begin
re:=Application.MessageBox('別名NewAlias不存在,現在創建嗎?','BDE信息窗口',mb_OKCancel);
if re=IDCANCEL then begin
sl.Free;
Exit;
end;
Session.AddStandardAlias('newAlias','c:/instance1','Paradox');
//增加一個名為NewAlias的數據庫別名
Session.SaveConfigFile; //保存BDE配置文件
end ;
sl.free;
end;
圖4 提示信息
1.1.4 Delphi連接數據庫的方式有哪些
---- Delphi訪問數據庫的方式主要有3種,包括直接訪問,如訪問標准數據庫類型Paradox和dBASE;通過ODBC訪問,如訪問Access、Foxpro等數據庫;通過內嵌(Native)方式訪問數據庫,如訪問SQLServer、Oracle、DB2等。下面以Table組件為例說明這3種方式的特點和用法。
1. 直接訪問
----Delphi可以直接訪問諸如Paradox和dBASE的標准數據庫類型,而不需要什么特別的設置,只需要把文件路徑或數據庫別名賦值給Table組件的屬性DatabaseName就可以訪問該路徑下的數據表了。
2. 通過ODBC訪問
----訪問Paradox和dBASED以外的數據庫,通常可通過ODBC來實現。Delphi可以訪問支持ODBC的數據庫系統,如Access、SQLServer和Oracle等。當然,通過ODBC訪問數據庫時,首先要使用Windows的控制面板或Delphi的數據庫引擎建立ODBC數據源。
3. 通過內嵌方式訪問
Delphi中還可以不使用ODBC,而以內嵌方式訪問SQLServer、Oracle、DB2等數據庫系統。這需要使用數據庫別名來指定數據庫,數據庫別名可以事先建立,也可以在程序運行時動態創建。前者稱為靜態別名,后者稱為動態別名。使用數據庫別名來訪問數據庫的方法和使用ODBC數據源的情形相同,這里不再贅述。
通過內嵌方式訪問數據庫的靜態別名必須在BDE中建立。以訪問SQLServer數據庫為例,在建立別名時必須指定數據庫服務器的名稱(Servername)、主機名(Hostname)以及要訪問的數據庫名稱(Databasename),可以指定登錄用戶名(Username)和密碼(Password)等。
圖5 設置Database組件
通過動態創建的別名來訪問數據庫必須使用Database組件。用戶可以用鼠標雙擊Database組件,出現參數設置窗口,如圖5所示。在“Drivername”一欄選擇要訪問的數據庫系統,如“MSSQL”,然后選擇按鈕“Defaults”,就會把BDE中該數據庫系統所需的參數名稱和缺省值加入到“Parameteroverrides”列表中。根據實際情況更改參數中的“Servername”、“Databasename”等項。最后單擊OK按鈕完成設置。
比較可知,通過內嵌方式訪問數據庫要比通過ODBC訪問數據庫速度快一些。而且,內嵌方式可以在程序中動態設置連接數據庫所需的參數,用戶可不必設置ODBC數據源,從而降低了對用戶技術水平的要求,並且減少了用戶的工作量。因此從系統配置的難易和復雜程度來看,使用內嵌方式開發出的數據庫應用系統更便於普通用戶使用。
1.1.5 如何獲取BDE的版本信息
單擊Delphi工具欄中的“New Form”按鈕,新建一窗體。向該窗體中添加1個Memo組件,並設置其Align屬性為alclient。在窗體對應的單元文件的USES部分添加dbierrs, DBTables兩個引用庫文件。在下面的單元文件中,fDbiGetSysVersion函數將返回一個稱為SysVersion的結構,並將最后結果顯示在窗體的Memo中。
/*該窗體的單元文件代碼*/
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,dbierrs, DBTables, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function fDbiGetSysVersion(SysVerList: TStringList): SYSVersion;
var
Month, Day, iHour, iMin, iSec: Word;
Year: SmallInt;
begin
Check(DbiGetSysVersion(Result));
if (SysVerList <> nil) then
begin
with SysVerList do
begin
Clear;
Add(Format('數據引擎版本號=%d', [Result.iVersion]));
Add(Format('接口級=%d', [Result.iIntfLevel]));
Check(DbiDateDecode(Result.dateVer, Month, Day, Year));
Add(Format('版本日期=%s', [DateToStr(EncodeDate(Year, Month, Day))]));
Check(DbiTimeDecode(Result.timeVer, iHour, iMin, iSec));
Add(Format('版本時間=%s', [TimeToStr(EncodeTime(iHour, iMin, iSec div 1000, iSec div 100))]));
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var hStrList: TStringList;
Ver: SYSVersion;
begin
hStrList:= TStringList.Create;
try Ver := fDbiGetSysVersion(hStrList);
except
ShowMessage('BDE not installed !');
end;
Memo1.Lines.Assign(hStrList);
hStrList.Destroy;
end;
end.
按F9鍵運行程序,將得到如圖6所示的BDE版本信息。
圖6 運行結果窗口
1.1.6 如何維護DBF數據庫
事實上,由於Delphi具有快捷易用、功能強大等優點,使得很多FoxPro和VFP的程序設計人員加入到了Delphi陣營。但在FoxPro和VFP的開發環境下,有許多DBF類型的數據表文件需要移植到Delphi中來,因此在Delphi中如何維護和操作這些數據將是一個很重要的問題。下面闡述在Delphi中完成DBF數據庫的如下操作:真正刪除記錄、顯示被刪除記錄、獲取當前記錄號和恢復被刪除記錄.
1. 真正刪除記錄
在Delphi程序中,使用Table或Query組件的Delete方法執行刪除記錄操作時,系統執行的是軟刪除,即相當於Foxpro中的“Set Delete Off”的效果。該方法只是將記錄用星號標記為刪除,實際並沒有進行物理上的刪除。要想真正刪除記錄,需要調用BDE函數DbiPackTable,該函數語法為:
functionDbiPackTable(hDb:hDBIDb;hCursor:hDBICur;pszTableName:PChar;pszDriverType:PChar;bRegenIdxs:Bool):DBIResult;
其中hDb為數據庫Databse的句柄,hCursor為數據表Table的句柄,pszTableName為要刪除記錄的數據表名,pszDriverType為要刪除記錄的數據表的類型,bRegenIdxs表示是否在刪除記錄后自動更新索引文件。在四個參數中,hDb不能為NULL。hCursor、pszTableName、pszDriverType可以為NULL,但必須提供足夠的信息來標識數據表的文件名和類型,當hCursor不為空時,pszTableName和pszDriverType可以為NULL;當pszTableName為數據表的路徑和文件名時,hCursor和pszDriverType可以為NULL。
值得注意的是,在刪除記錄時,如果用Table來實現,則Table必須以Exclusive=True的方式打開。
2. 顯示或不顯示軟刪除記錄
當DBF數據庫中的記錄執行軟刪除后,默認情況下在DBGrid等數據庫顯示組件中是看不見這些記錄的。可以用BDE函數來控制是否顯示DBF數據庫中被軟刪除的記錄,如同在Foxpro中利用語句Set Delete ON/OFF開關一樣。執行這一功能的函數為DbiSetProp,其語法為:
functionDbiSetProp(hObj:hDBIObj;iProp:Longint;iPropValue:Longint):DBIResult;
該函數用來設置DBI對象中某個屬性的值。其中Obj為DBI對象名稱,這里為數據表Table的句柄,iProp為屬性名,可使用軟刪除屬性curSOFTDELETEON,iPropValue為屬性值。
3. 獲取當前記錄號
在Delphi中可以使用BDE函數獲取當前記錄在數據集中的記錄號.該函數為DbiGetRecord,其語法為:
functionDbiGetRecord(hCursor:hDBICur;eLock:DBILockType;pRecBuff:Pointer;precProps:pRECProps):DBIResult;
該函數用來取得當前記錄的一些屬性。其中,hCursor為數據集的Handle,eLock為對記錄加鎖的類型,pRecBuff存放記錄的緩沖區,precProps為記錄屬性集,這里面就包含當前記錄的記錄號。
4. 恢復軟刪除記錄
在Delphi應用程序中,對DBF數據表執行的刪除操作為軟刪除操作。由於物理記錄並沒有從數據表中刪除,因此可以恢復被軟刪除的記錄,只要去掉刪除標志即可。完成這一功能需使用函數DbiUndeleteRecord,其語法為:
functionDbiUndeleteRecord(hCursor:hDBICur):DBIResult;
其中,hCursor可以是數據集的Handle。
1.1.7 如何備份數據表
對於數據庫應用來說,數據表的備份應該算是一個常用的功能。下面舉一例,介紹其實現過程。單擊Delphi工具欄上的“New Form”按鈕,新建一窗體,在該窗體上添加1個Table組件、1個DataSource組件、1個DBGrid組件和2個Button組件,設置數據庫之間的連接關系,並置Table的Active屬性為True。窗體的布局設計如圖7所示。
圖7 窗體布局示例
然后在“備份表”按鈕的OnClick事件中添加代碼:
Procedure TForm1.Button1Click(Sender: TObject);
begin
QuickCopyTable(table1,'c:/a.dbf',true);
ShowMessage('數據表備份已完成')
end;
其中所引用的QuickCopyTable方法的代碼如下所示。
Procedure QuickCopyTable(T: TTable; DestTblName: string; Overwrite: Boolean);
var
DBType: DBINAME;
WasOpen: Boolean;
NumCopied: Word;
begin
WasOpen := T.Active; //保存表的Active屬性值
if not WasOpen then T.Open; //檢查表是否打開
Check(DbiGetProp(hDBIObj(T.Handle),drvDRIVERTYPE,@DBType, SizeOf(DBINAME), NumCopied)); //獲得驅動程序類型字符串
Check(DBICopyTable(T.DBHandle, Overwrite, PChar(T.Tablename),
DBType, PChar(DestTblName))); //復制表
T.Active := WasOpen; //恢復表的Active屬性值
end;
最后,要在單元文件的Uses備份添加dbierrs庫文件。此時單擊Delphi工具欄上的“Run”按鈕或按下F9鍵,運行程序,單擊“備份表”按鈕,如圖8所示。所備份的數據表a.dbf內容,如圖9所示。
圖8 運行結果窗口
圖9 備份的a.dbf文件的內容
1.1.8 如何使用過濾器
在操作數據庫時,經常需要對數據進行篩選過濾。例如在第四章學生學籍管理系統的案例中,有一個名為jiben.dbf的數據表,它具有XH、XM、CSRQ等多個字段,如果只想查看班級編號為001的學生記錄,就需要對數據表中的信息進行過濾。下面是常用的對數據表信息進行過濾的方法:
1. 利用TTable和TQuery的Filter屬性
n 在設計時設置Filter屬性
例如設置Filter為(bjbh=’001’),然后改變Filtered屬性為True。此時將只能看到對應的bjbh(班級編號)字段內容為’001’的記錄。另外設置Filter時可以使用的操作符有:<、>、<=、>=、=、<>、AND、OR、NOT。
n 在程序運行期間進行動態過濾
要在程序運行時改變Filter屬性,包括兩種情況。一是操作符右邊為常量,例如語句(table1.Filter:='bjbh'+'='+'001';);二是操作符右邊不為常量,可能是通過一個變量指定的值,或由一輸入框給出的值。此時需要使用Format函數。其代碼形式為(Table1.Filter:=Format('bjbh'+'='+'%S',[bjbhvalue]);)其中bjbhvalue為已經賦值的一個字符串變量,也可以為其他形式如Edit1.Text。
2. 用ApplyRange篩選數據集的記錄
Delphi的DBDEMOS數據庫中有一個Customer.db數據表,如果我們想查看顧客號從1000到3000之間的顧客記錄。可使用ApplyRange, SetRangeStart, SetRangeEnd過程來完成,其代碼為:
Table1.SetRangeStart;
Table1[‘CustNo’]:=1000;
Table1.SetRangeEnd;
Table1[‘CustNo’]:=3000;
Table1.ApplyRange;
需要說明的是該過程只適用於索引的字段。
3. 用OnFilterRecord事件過濾
例如下面的代碼:
Procedure TForm1.Table1FilterRecord(DataSet:TDataSet;varAccept:Boolean);
begin
Accept:=DataSet[‘bjbh’]=’001’;
end;
4. 用Query組件的SQL語句
n SQL語句中不包含變量和參數
Select * from Customer Where CustNo>=1000 and CustNo<=3000
n SQL語句中包含參數
Select * from Customer Where CustNo>=:p1
可在程序中給參數p1賦值,讀者可參閱前面講述的案例程序。
n SQL語句中包含變量
此時向Query組件添加SQL語句的代碼如下所示:
Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add(Format(‘Select * from Customer’+’’+’where CustNo=’+’%S’, [CustNoValue]));
Query1.Open;
1.2 關於字符串操作
在程序開發過程中,經常要涉及對字符串的操作。Delphi提供了一個TStrings類,用來完成存儲和操作字符串,它的主要行為有:
1. 在列表中添加或刪除字符串。
2. 重新排列字符串。
3. 在指定的位置存取字符串。
4. 從文件或流中讀取及寫入字符串
5. 為列表中的每一個字符串聯系一個對象。
TStrings類的繼承關系為:TObject-TPersistent
1.2.1 常用的字符串處理函數有哪些
表1列出了常用的字符串處理函數。
表1 常用字符串處理函數
函數名
|
語法
|
功能
|
AnsiCompareStr
|
function AnsiCompareStr(const S1, S2: string): Integer;
|
用於比較兩個大小寫敏感的字符串
|
AnsiCompareText
|
function AnsiCompareText(const S1, S2: string): Integer;
|
用於比較兩個大小寫不敏感的字符串
|
AnsiUpperCase
|
function AnsiUpperCase(const S: string): string;
|
將字符串轉換為全部大寫
|
AnsiLowerCase
|
function AnsiLowerCase(const S: string): string;
|
將字符串轉換為全部小寫
|
Appendstr
|
procedure AppendStr(var Dest: string; const S: string); deprecated;
|
將給定字符串常量添加到目標字符串末尾
|
CompareStr
|
function CompareStr(const S1, S2: string): Integer;
|
用於比較兩個大小寫敏感的字符串,其結果與區域設置無關
|
CompareText
|
function CompareText(const S1, S2: string): Integer;
|
用於比較兩個大小寫不敏感的字符串,其結果與區域設置無關
|
Concat
|
function Concat(s1 [, s2,..., sn]: string): string;
|
將一組字符串連接起來
|
Copy
|
function Copy(S; Index, Count: Integer): string;
|
返回字符串的子串
|
Delete
|
procedure Delete(var S: string; Index, Count:Integer);
|
從字符串中刪除一個子串
|
Insert
|
procedure Insert(Source: string; var S: string; Index: Integer);
|
在字符串的指定位置插入一個子串
|
Length
|
function Length(S): Integer;
|
返回字符串中中字符的個數
|
Pos
|
function Pos(Substr: string; S: string): Integer;
|
在字符串中搜索子串,返回的是索引值
|
LeftStr
|
function LeftStr(const AText: AnsiString; const ACount: Integer): AnsiString;
|
返回從字符串左邊開始指定長度的子串
|
RightStr
|
function RightStr(const AText: AnsiString; const ACount: Integer): AnsiString;
|
返回從字符串末尾向前指定長度的子串
|
InttoStr
|
function IntToStr(Value: Integer): string;
|
將整數轉換為字符串
|
StrtoInt
|
function StrToInt(const S: string): Integer;
|
將字符串轉換為整數
|
LowerCase
|
function LowerCase(const S: string): string;
|
轉換為小寫
|
UpperCase
|
function UpperCase(const S: string): string;
|
轉化為大寫
|
Val
|
procedure Val(S; var V; var Code: Integer);
|
將字符串的值轉換為其數字表示式
|
1.2.2 TStrings對象的屬性和方法有哪些
TStrings的一些重要屬性和方法如下所示。
Count:該屬性定義列表中字符串的數量。
Strings:該屬性表示由參數Index指定位置的字符串。0表示第一個字符串,1表示第二個字符串,依此類推。
Text:TStings對象的文本,它包含一些由回車和換行符分開的字符串。
Add方法:該方法在字符串列表的末尾添加一字符串。在調用Add方法加入字符串之后,再返回新字符串的索引值。
AddObject方法:該方法向字符串列表中加入一個字符串及與它相聯系的對象。調用AddObject方法之后將返回新字符串和對象的索引值。
Append方法:該方法將在字符串列表中添加一字符串,它與Add方法一樣,但不返回值。
Clear方法:該方法將清空字符串列表。
Delete方法:刪除指定的字符串。
Destroy方法:析構函數,毀壞一個TStrings對象實例。
IndexOf方法:function IndexOf(const S:string):integer; 該方法的功能是返回字符串S在字符串列表中的索引值。調用IndexOf函數返回的是第一次發現字符串S的位置。其返回值也以0作為起點,若返回0則表示是第一個字符串,返回1表示是第二個字符串,依此類推。如果指定的字符串S不在列表中,則返回-1。需要指出的是,若S在字符串列表中出現的次數大於1,則IndexOf方法返回的是第一次發現的位置值。下面介紹一個IndexOf方法的應用實例。
單擊Delphi工具欄上的“New Form”按鈕,新建一窗體,在該窗體上添加1個FileListBox組件、1個DirectoryListBox組件和2個Label組件,完成各個組件的屬性設置及布局之后,在DirectoryListBox組件的OnChange事件中添加如下所示的代碼。然后按F9鍵執行程序,其結果如圖10所示。
圖10 執行結果窗口
Procedure TForm1.DirectoryListBox1Change(Sender: TObject);
begin
filelistbox1.Directory:=directorylistbox1.Directory;
if filelistbox1.Items.IndexOf('regedit.exe')>-1 then
showmessage('當前是Windows目錄');
end;
Insert方法:該方法在指定位置插入一字符串。其參數Index為給定的位置索引值,參數S為要插入的字符串。
LoadFromFile方法:調用該方法將使用指定的文件填充文本。其參數FileName用來定義文件名,文件中行與行之間以回車和換行符隔開。事實上,LoadFromFile方法是運用Add方法將文件中的每一行添加到字符串列表中的。
SaveToFile方法:與LoadFromFile方法相對應,該方法將列表中的字符串存儲於文件中,其參數FileName用來定義文件名。
1.2.3 如何判斷輸入的字符串是電子郵件地址
選擇“File|New Application”菜單命令,在默認建立從窗體中添加1個Label組件、1個Edit組件和1個Bitbtn組件,完成各個組件的屬性設置即布局之后,在Bitbtn組件的OnClick事件中加入如下所示的代碼。
Procedure TForm1.BitBtn1Click(Sender: TObject);
begin
if isemail(Edit1.Text) then
ShowMessage('正確的郵件地址!')
else
showmessage('錯誤的郵件地址!');
end;
其中isemail函數的代碼如下所示,可將其添加到單元文件的implementation部分。
Function IsEMail(EMail: String): Boolean;
var
s: String;
ETpos: Integer;
begin
ETpos:= pos('@', EMail); //獲取@符號的位置
if ETpos > 1 then
begin
s:= copy(EMail,ETpos+1,Length(EMail)); //截取子串
if (pos('.', s) > 1) and (pos('.', s) < length(s)) then
Result:= true
else
Result:= false;
end
else
Result:= false;
end;
按下F9鍵執行程序,其結果如圖11和9-12所示。
圖11 用戶輸入錯誤的郵件地址窗口
圖11 用戶輸入正確的郵件地址窗口
1.2.4 如何獲得字符串中漢字和英文的個數
Microsoft Word具有一項“字數統計”功能,可以統計出文本中漢字和英文的個數,如圖12所示。
圖12 Microsoft Word的字數統計功能
其設計思路就是通過把字符轉換為ASCII碼數值來進行判斷,Ord函數就可以把字符轉換為對應的ASCII碼數值,其中值為33-126為鍵盤可使用字符,值127以上的為未知字符,即漢字。下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Memo組件、2個Label組件和2個Button組件,完成對各個組件的屬性設置及布局之后,在“字數統計”按鈕的OnClick事件中添加如下所示的代碼。
Procedure TForm1.Button1Click(Sender: TObject);
var s:string;
I,e,c:integer;
begin
s:=memo1.text; //Memo的內容
e:=0;c:=0;
for I:=1 to length(s) do
begin
if (ord(s[I])>=33)and(ord(s[I])<=126) then
begin
inc(e);
label1.caption:='英文字數:'+inttostr(e);
end
else
if (ord(s[I])>=127) then
begin
inc(c);
label2.caption:='中文字數:'+inttostr(c div 2);
end;
end;
end;
按下F9鍵運行程序,其執行結果如圖13所示。
圖13 執行結果窗口
1.2.5 使用拼音首字母序列實現檢索功能
在本書的考勤管理系統中,為了查詢中文人名,我們在相應的數據表中添加了姓名編碼字段,即提取員工姓名的首字母,作為其姓名編碼。這樣作的目的實質上就是為了方便查詢功能的實現。本節提供的該問題解決途徑在某種程度上可以進一步完善考勤信息管理系統案例。
事實上,解決這個問題的思路很簡單。那就是要找出漢字表中拼音首字母分別為“A”到“Z”的漢字內碼范圍,這樣對於要檢索的漢字,只需要檢查它的內碼位於哪一個首字母的范圍內,就可以判斷出它的拼音首字母。
下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Label組件、1個Bevel組件、1個Edit組件和2個ListBox組件。完成對各個組件的屬性設置及布局之后,在Edit組件的OnChange事件中添加如下所示的代碼。
Procedure TForm1.Edit1Change(Sender: TObject);
var ResultStr:string;
begin
ResultStr:='';
listbox2.Items.Text := SearchByPYIndexStr(listbox1.Items, edit1.Text);
end;
這里所使用的SearchByPYIndexStr函數的代碼如下所示,其功能是在字符串列表中檢索符合拼音索引字符串的所有字符串。可將其添加到窗體單元文件的implementation部分。
Function SearchByPYIndexStr( SourceStrs:TStrings; PYIndexStr:string):string;
label NotFound;
var
i, j :integer;
hzchar :string;
begin
for i:=0 to SourceStrs.Count-1 do
begin
for j:=1 to Length(PYIndexStr) do
begin
hzchar:=SourceStrs[i][2*j-1]+ SourceStrs[i][2*j];
if (PYIndexStr[j]<>'?') and (UpperCase(PYIndexStr[j]) <>
GetPYIndexChar(hzchar)) then
goto NotFound;
end;
if result='' then
result := SourceStrs[i]
else
result := result + Char(13) + SourceStrs[i];
NotFound:
end;
end;
上面程序中引用的GetPYIndexChar函數用於獲取指定漢字的拼音索引字母,如“杜”的索引字母是“D”。該函數的代碼如下所示,也將其添加到窗體單元文件的implementation部分。
Function GetPYIndexChar( hzchar:string):char;
begin
case WORD(hzchar[1]) shl 8 + WORD(hzchar[2]) of
$B0A1..$B0C4 : result := 'A';
$B0C5..$B2C0 : result := 'B';
$B2C1..$B4ED : result := 'C';
$B4EE..$B6E9 : result := 'D';
$B6EA..$B7A1 : result := 'E';
$B7A2..$B8C0 : result := 'F';
$B8C1..$B9FD : result := 'G';
$B9FE..$BBF6 : result := 'H';
$BBF7..$BFA5 : result := 'J';
$BFA6..$C0AB : result := 'K';
$C0AC..$C2E7 : result := 'L';
$C2E8..$C4C2 : result := 'M';
$C4C3..$C5B5 : result := 'N';
$C5B6..$C5BD : result := 'O';
$C5BE..$C6D9 : result := 'P';
$C6DA..$C8BA : result := 'Q';
$C8BB..$C8F5 : result := 'R';
$C8F6..$CBF9 : result := 'S';
$CBFA..$CDD9 : result := 'T';
$CDDA..$CEF3 : result := 'W';
$CEF4..$D188 : result := 'X';
$D1B9..$D4D0 : result := 'Y';
$D4D1..$D7F9 : result := 'Z';
else
result := char(0);
end;
end;
完成之后,按下F9鍵運行程序,用戶在編輯框中輸入人名的拼音首字母后,在ListBox2中會自動檢索對應的姓名,如圖14所示。
圖14 執行結果窗口
1.3 關於Delphi的VCL
OOP系統開發的核心是組件的使用,Delphi所提供的VCL類似於Visual C++的MFC和Borland C++的OWL類庫,但C++的類庫在可視化程序設計、易用性等方面遠不如VCL庫。用戶使用VCL庫可以快速制作系統交互界面,實現本地和遠程數據庫的存取,設計應用程序模板和ActiveX控件等。本節主要介紹一些有關VCL的技巧及應用。
1.3.1 深入了解RichEdit組件
RichEdit組件提供了一個標准的、豐富的文本操作界面,該組件允許用戶輸入不同字體屬性和不同段落屬性的文本。RichEdit組件與Memo組件非常相似,都可以編輯多行文本,但Memo編輯器中的文本只能有一種格式,而RichEdit組件中的文本卻可以包含多種字體和顏色。
1.3.1.1 如何獲得RichEdit組件的當前行號
使用RichEdit組件的lines.count屬性可以得到該組件中文本的總行數,但不能知道光標當前所在行號,而要實現這個功能,則需調用em_ LineFromChar消息。
下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個RichEdit組件和1個Button組件。完成對各個組件的屬性設置及布局之后,在Button組件的OnClick事件中添加如下所示的代碼。
Procedure TForm1.Button1Click(Sender: TObject);
var
CurrentLine:Integer;
begin
CurrentLine:=richedit1.Perform(EM_LineFromChar,richedit1.SelStart,0)+1;
Application.MessageBox(PChar('當前行號是'+IntToStr(CurrentLine)),'消息',mb_icon information);
end;
需要說明的是,由於EM_LineFromChar消息返回的行號中第一行為0,所以要在代碼中加1。按下F9鍵運行程序,然后單擊窗口中的Button按鈕,其結果如圖15所示。
圖15 執行結果窗口
1.3.1.2 如何執行撤銷操作
對於Memo組件來說,實現撤銷功能是不需添加代碼的,只需清除其Popupmenu屬性,這樣在程序運行時就能用鼠標右鍵激活一個快捷菜單,如圖16所示,選擇該菜單中的“撤銷”命令即可。
圖16 Memo的快捷菜單
而RichEdit組件卻不能這樣完成撤銷操作。下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個RichEdit組件和1個PopupMenu組件。雙擊PopupMenu組件,添加菜單項“撤銷”,並在該菜單項的OnClick事件中添加如下所示的代碼。
Procedure TForm1.N1Click(Sender: TObject);
begin
RichEdit1.Perform(EM_UNDO,0,0); //執行撤銷操作
end;
需要說明的是,在執行撤銷操作之前還要檢查是否允許撤銷,從而開啟或關閉快捷菜單中的“撤銷”命令,因此需在RichEdit1的OnChange事件中添加如下所示的代碼。
Procedure TForm1.RichEdit1Change(Sender: TObject);
begin
N1.Enabled:=RichEdit1.Perform(EM_CANUNDO,0,0)<>0;
end;
按下F9鍵運行程序,然后右鍵單擊窗口中的RichEdit區域,彈出如圖17所示的快捷菜單,選中部分文本,按下Delete鍵,此時再右鍵單擊窗口中的RichEdit區域,將彈出如圖18所示的快捷菜單,此時“撤銷”命令已激活,可以完成相應功能。
圖17 “撤銷”命令失效 圖18 “撤銷”命令激活
1.3.2 如何調整Memo中Tab的距離
使用制表鍵Tab在文本編輯器移動是用戶頻繁使用的操作,在Microsoft Word中可以自定義Tab移動的距離,那么在Delphi中如何實現對文本編輯類組件如Memo中Tab距離的控制呢?下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Memo組件,然后在窗體的OnCreate事件中添加如下所示的代碼。
Procedure TForm1.FormCreate(Sender: TObject);
var
DialogUnitsX : LongInt;
PixelsX : LongInt;
i : integer;
TabArray : array[0..4] of integer;
begin
Memo1.WantTabs := true;
DialogUnitsX := LoWord(GetDialogBaseUnits); //返回對話框的基本單位
PixelsX := 30; //可設置該值決定Tab移動距離
for i := 1 to 5 do begin
TabArray[i - 1] :=
((PixelsX * i ) * 4) div DialogUnitsX; //計算TabArry
end;
SendMessage(Memo1.Handle, EM_SETTABSTOPS,5, LongInt(@TabArray));
//發送設置Tab距離消息
Memo1.Refresh; //刷新Memo
end;
按下F9鍵運行程序,然后在Memo中使用Tab鍵,如圖19所示,圖中使用了不同的PixelsX值。
圖19 PixelsX=20和50的執行結果窗口
1.3.3 如何使用TApplication類
前面我們已經用到過TApplication類的一些屬性和方法,如Application.messagebox等,這里作一個匯總。
1. 檢測當前Windows程序是否被激活
TApplication類有一個Active屬性,該屬性用來描述當前運行的程序是否被激活。可以使用如下所示的代碼進行檢測:
If Application.Active=False then
ShowMessage(’當前窗口沒有被激活’);
2. 取得當前程序名
TApplication類的EXEName屬性可以返回該可執行程序的完整文件名(包括路徑)。其代碼如下所示:
ShowMessage(Application.ExeName);
3. 更改程序最小化時的標題
使用TApplication類的Title屬性。可以控制程序最小化時的標題,而窗口中標題欄的標題則是由Form的Caption屬性決定的。另外,在本書第1章曾經提到在設計期通過使用“Project|Options”菜單命令來設置應用程序的標題,實質上也是修改TApplication類的Title屬性。其代碼如下所示:
Application.Title:=’程序的標題’;
Form1.Caption:=’窗口的標題’;
4. 標識程序的主窗口
Windows應用程序一般都有一個主窗口。可使用TApplication類的MainForm屬性返回程序的主窗口。
5. 彈出提示框
本書前面章節已經提到了TApplication類的MessageBox 函數,這里不再贅述。
6. 控制窗口的尺寸
可以用Application的事件來調整窗口大小。主要是使用以下兩個過程:
Application.Minimized;
Application.Restore;
前一個過程用來將程序的主窗口最小化,而后一個過程用來將最小化的窗口恢復到原來的尺寸。
7. 鏈接聯機幫助文件
TApplication的CurrentHelpFile屬性能夠指定當前程序所用的幫助文件的文件名。這個屬性經常與另一個方法聯合在一起使用。通過它們的命令組合,就可以使系統彈出一個顯示某主題的聯機幫助文件。
例如:Application.HelpFile := '聯機幫助文件名';
Application.HelpJump('聯機幫助文件的主題’)
8. 終止程序
盡管可以使用關閉主窗口的方法終止程序,但更好的辦法是使用Application的Terminate過程。
1.3.4 通用的VCL屬性有哪些
很多組件都有共同的屬性,應該說了解VCL的公共屬性對於程序設計是非常重要的。表2列出了一些通用屬性。
表2 VCL公共屬性
屬性
|
范圍
|
含義
|
Action |
某些組件 |
指定與組件連接的活動對象 |
Align |
某些組件 |
確定組件的對齊方式 |
Anchors |
大部分組件 |
說明與組件連接的窗體位置點 |
Autosize |
某些組件 |
確定組件是否依據內容決定本身大小 |
BiDiMode |
所有組件 |
支持自右向左輸入的語言 |
BorderWidth |
窗口組件 |
定義邊框寬度 |
BoundsRect |
所有組件 |
定義運行期組件的邊框矩形 |
Caption |
大部分組件 |
定義組件標題 |
ComponentCount |
所有組件 |
定義當前組件擁有的子組件數目(在運行期調用) |
ComponentIndex |
所有組件 |
定義當前組件中子組件的索引位置(在運行期調用) |
Components |
所有組件 |
定義當前組件擁有的子組件數組,可用索引值調用 |
Constraints |
所有組件 |
在更改組件大小時先哲組件的最大和最小尺寸 |
Color |
大部分組件 |
定義當前組件的表面顏色或背景顏色 |
Ctrl3D |
大部分組件 |
定義組件是否具有三維效果 |
Cursor |
所有組件 |
定義當前組件上的鼠標形狀 |
DragCursor |
大部分組件 |
定義當前組件在拖動時的鼠標形狀 |
DragKind |
大部分組件 |
若DragMode為自動,則可選擇拖動還是停放 |
DragMode |
大部分組件 |
定義組件的拖放行為 |
Enabled |
所有組件 |
定義組件是否激活 |
Font |
所有組件 |
定義組件內顯示的文本字體 |
Handle |
窗口類組件 |
定義窗口句柄 |
Height |
所有組件 |
定義組件的高度 |
Hint |
所有組件 |
定義提示文本 |
Left |
所有組件 |
定義組件左上角的水平坐標值 |
Name |
所有組件 |
定義組件名 |
Owner |
所有組件 |
定義主組件 |
PopupMenu |
所有組件 |
定義用戶右鍵單擊時彈出的快捷菜單 |
ShowHint |
所有組件 |
定義是否激活提示文本 |
TabOrder |
窗口類組件 |
定義父組件中的切換順序 |
Tag |
所有組件 |
用於存儲定制的非定義數據的長整型變量 |
Top |
所有組件 |
定義組件左上角的垂直坐標 |
Visible |
所有組件 |
定義組件是否可視 |
Width |
所有組件 |
定義當前組件的寬度 |
1.3.5 通用的VCL方法有哪些
VCL的方法由函數和過程組成。表3列出了一些通用的組件對象方法。
表3 VCL 通用方法
屬性
|
含義
|
BeginDrag |
開始手工拖動 |
BringToFront |
將組件置於其他組件前 |
CanFocus |
確定是否允許組件接收焦點 |
ClientToScreen |
將客戶區坐標轉換為屏幕坐標 |
ContainsControl |
確定某組件是否屬於當前組件 |
Create |
建立組件的實例 |
Destroy |
析構函數,毀壞實例 |
Dragging |
說明組件是否被拖動 |
EndDrag |
終止手工拖動 |
ExecuteAction |
激活與組件相連的操作 |
FindComponent |
返回Components數組中具有給定名稱的組件 |
Focused |
確定組件是否具有焦點 |
Free |
從內存中清除對象 |
GetTexBuf |
檢索組件的文本(或標題) |
GetTextLen |
返回組件的文本(或標題) |
Hide |
隱藏組件 |
InsertComponent |
向組件列表中添加新組件 |
Invalidate |
強制重新繪制組件 |
ManualDock |
手工激活停放 |
ManualFloat |
將停放組件設置為浮動組件 |
RemoveComponent |
從Components列表中刪除一個組件 |
ScaleBy |
用指定的百分比設置組件的比例 |
ScreenToClient |
將屏幕坐標轉換為客戶區坐標 |
ScrollBy |
滾動顯示組件內容 |
SendToBack |
將當前組件置於其他組件后 |
SetBounds |
改變組件的位置和大小 |
SetFocus |
賦予組件輸入焦點 |
SetTextBuf |
設置組件的文本(或標題) |
Show |
顯示組件 |
Update |
重新繪制組件 |
1.4 綜合
本節列舉了一些程序設計過程中經常遇到的問題解答,作為前面案例學習的補疑。
1. 在Delphi中如何調用外部的EXE文件
首先在單元文件的Uses語句中添加WinProcs,然后在需要調用外部EXE文件的地方添加代碼:
begin
WinExec(......);
end.
WinExec為API函數,其語法為:UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow );
2. 如何啟動控制面板中的各項設置功能
在本書第3章的案例中曾經使用WinExec這個API函數啟動了控制面板中的“系統信息”功能,而啟動控制面板中的其他選項功能,如“添加/刪除程序”、“顯示”等,也可以使用如下所示的語句來完成(以中文Windows 2000操作系統為例)。
…
var x:cardinal; //定義變量
begin
//啟動“控制面板”
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL',9);
//啟動“輔助功能選項”的“鍵盤”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1',9);
//啟動“輔助功能選項”的“聲音”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2',9);
//啟動“輔助功能選項”的“顯示”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3',9);
//啟動“輔助功能選項”的“鼠標”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4',9);
//啟動“輔助功能選項”的“常規”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5',9);
//啟動“添加/刪除程序”的“添加新程序”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Appwiz.cpl,,1',9);
//啟動“添加/刪除程序”的“添加/刪除Windows組件”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Appwiz.cpl,,2',9);
//啟動“添加/刪除程序”的“更改或刪除程序”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Appwiz.cpl,,3',9);
//啟動“顯示”的“背景”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0',9);
//啟動“顯示”的“屏幕保護程序”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1',9);
//啟動“顯示”的“外觀”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2',9);
//啟動“顯示”的“設置”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3',9);
//啟動“Internet”的“常規”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,0',9);
//啟動“Internet”的“安全”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,1',9);
//啟動“Internet”的“內容”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,2',9);
//啟動“Internet”的“連接”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,3',9);
//啟動“Internet”的“程序”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,4',9);
//啟動“Internet”的“高級”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Inetcpl.cpl,,5',9);
//啟動“區域選項”的“常規”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,0',9);
//啟動“區域選項”的“數字”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,1',9);
//啟動“區域選項”的“貨幣”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,2',9);
//啟動“區域選項”的“時間”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,3',9);
//啟動“區域選項”的“日期”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,4',9);
//啟動“區域選項”的“輸入法區域設置”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Intl.cpl,,5',9);
//啟動“游戲選項”的“控制器”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Joy.cpl,,0',9);
//啟動“游戲選項”的“控制器ID”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Joy.cpl,,1',9);
//啟動“鼠標”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Main.cpl',9);
//啟動“聲音和多媒體”的“聲音”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Mmsys.cpl,,0',9);
//啟動“聲音和多媒體”的“音頻”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Mmsys.cpl,,1',9);
//啟動“聲音和多媒體”的“硬件”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Mmsys.cpl,,2',9);
//啟動“電話和調制解調器選項的”的“調制解調器”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Modem.cpl',9);
//啟動“掃描儀和照相機”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Sticpl.cpl',9);
//啟動“系統”的“常規”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,0',9);
//啟動“系統”的“網絡標識”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,1',9);
//啟動“系統”的“硬件”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,2',9);
//啟動“系統”的“用戶配置文件”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,3',9);
//啟動“系統”的“高級”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Sysdm.cpl,,4',9);
//啟動“日期和時間”的功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL timedate.cpl',9);
//啟動“電源選項”的功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Powercfg.cpl',9);
//啟動“電話和調制解調器”的“撥號規則”功能
x:=winexec('rundll32.exe shell32.dll,Control_RunDLL Telephon.cpl',9);
if x=0 then
messagebox(0,'程序超出內存','錯誤',0);
if x=ERROR_BAD_FORMAT then
messagebox(0,'該程序是一個非法的Win32程序).','錯誤',0);
if x=ERROR_FILE_NOT_FOUND then
messagebox(0,'指定文件沒找到','錯誤',0);
if x=ERROR_PATH_NOT_FOUND then
messagebox(0,'指定路徑沒找到','錯誤',0);
end;
3. 如何實現對數據庫中字段數據的模糊查詢
在SQL的查詢命令Select語法中,用LIKE可以使用兩個很特殊的通配符“%”和“_”(下划線)其中百分比符號可以匹配任何長度的字符串,而下划線只能匹配單個字符,如果能在數據庫查詢設計中靈活使用這兩個通配符,就可以對數據表中的字段進行模糊查詢。例如,要查詢學生基本信息表(jiben.dbf)中姓名(XM)字段所有“王”姓學生:
Select * From jiben where xm like ‘王%’
若需要查詢“王”姓學生且其姓名只有兩個字時,可使用下面的語句:
Select * From jiben where xm like ‘王_’
若要查詢email地址字段,以Q或L開頭的記錄,可使用下面的語句:
Select * From jiben where email like ‘[ql]%’
需要說明的是,如要查詢百分比符號或下划線字符本身,則需要使用方客戶將它們括起來。
4. Delphi有哪些快捷鍵
快捷鍵的使用能加快程序開發速度,節省程序設計人員的時間。
n 對窗體中組件的操作
Ctrl+UP:向上移動當前組件(精確);
Ctrl+Left:向左移動當前組件(精確);
Ctrl+Down:向下移動當前組件(精確);
Ctrl+Right:向右移動當前組件(精確);
以上的快接鍵,再加上Shift進行組合,例如Ctrl+Shift+Right即可實現對組件位置的粗略調整。
Shift+UP:減小當前組件的高度;
Shift+Left:減小當前組件的寬度;
Shift+Down:增加當前組件的高度;
Shift+Right:增加當前組件的寬度;
n 對Object Inspector的操作
F11:切換到Object Inspector,若連續按F11則將實現在Object Inspector、Form和Code Editor之間的切換;
Ctrl+Down:下拉當前窗體的組件列表;
Ctrl+Enter:編輯帶“…”的屬性值(如組件的Font屬性);
Alt+Down:下拉組件當前屬性選項隊列(如Align->alNone,alLeft,alRight等);
Ctrl+Tab:在屬性列表及事件列表中切換。
5. 如何選擇被組件覆蓋了的窗體
當窗體中某個組件的Align屬性設置為alClient時,此時選擇窗體則變得較麻煩,事實上,可以通過使用以下的方式選擇被組件覆蓋了的窗體。
n 在Object TreeView中選擇窗體;
n 按Esc鍵,一層一層選取,直到窗體被選中為止;
n 按Shift的同時單擊鼠標左鍵,一步即可選中窗體;
n 按F11選定Object Inspector,然后切換到屬性列表,再用Ctrl+Down快捷鍵。
6. 如何解決打開一個DBF數據表時出現的“Index not Found…”錯誤
當用戶創建一個DBF表時,若使用了MDX格式的索引文件,那么DBF表的表頭中的某個字節就自動設置了一個Flag。下次試圖重新打開這個DBF表的時候,BDE會自動識別這個Flag,從而試圖打開相應的MDX文件,若相應的MDX文件不存在,則會產生運行期錯誤。解決的方法是將Flag處的值置零。下面完成一個示范程序,選擇“File|New Application”菜單命令,在所創建的默認窗體中添加1個Table組件和1個Button組件,該窗體的單元文件代碼如下所示。
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, DB, DBTables, Grids, DBGrids;
type
TForm1 = class(TForm)
Table1: TTable;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
const
TheTableDir = 'c:/temp/';
TheTableName = 'jiben.dbf';
procedure RemoveMDXByte(dbFile: String);
const
Value: Byte = 0;
var
F: File of byte;
begin
AssignFile(F, dbFile);
Reset(F);
Seek(F, 28);
Write(F, Value);
CloseFile(F);
end;
procedure TForm1.Button1Click(Sender: TObject);
//試圖打開一個表,若不存在相應的MDX文件,將對表文件進行處理,並嘗試再次打開。
begin
try
Table1.DatabaseName :=TheTableDir; //設置表路徑
Table1.TableName := TheTableName; //設置表名
Table1.Open; //打開表
except
on E:EDBEngineError do
//若未發現MDX文件,將返回以下錯誤信息
if Pos('Index does not exist. File', E.Message)>0 then begin
//詢問用戶是否繼續
MessageDlg('MDX file not found. Attempting to open
without index.', mtWarning, [mbOk], 0);
RemoveMDXByte(TheTableDir + TheTableName); //從表頭中移去相應標志
PostMessage(Button1.Handle, cn_Command, bn_Clicked, 0);
end;
end;
end;
end.
7. 如何實現界面色彩漸變效果
界面色彩漸變效果是通過用漸變的畫刷在Canvas上繪制依次相鄰的矩形塊實現的。下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Button組件,然后在按鈕的OnClick事件中添加如下所示的代碼。
Procedure TForm1.Button1Click(Sender: TObject);
var i,j:Integer;
Dct:TRect;
begin
j:=Form1.height; //獲得窗體高度
for i:=0 to 255 do
begin
Canvas.Brush.Color:=RGB(255,0,i); //設置畫刷顏色
Dct:=Rect(i*2,0,(i+1)*2,j); //定義繪制的矩形區域
Canvas.FillRect(Dct); //填充顏色
end;
end;
按下F9鍵運行程序,單擊窗體中的Button按鈕,此時窗體將顯示由左到右紅色漸變的效果。
8. 如何實現圖形整體的拉出效果
其設計思路很簡單,就是動態的改變圖形區域的大小。需要注意的是要設置圖形組件的Stretch屬性為True。下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Image組件和1個Timer組件,並設置Image組件的Width屬性為0,Timer組件的Interval屬性為200,Enabled屬性為True。然后在Timer組件的OnClick事件中添加如下所示的代碼。
Procedure TForm1.Timer1Timer(Sender: TObject);
begin
Image1.width:=Image1.width+10; //設置增量
if image1.width=300 then //圖形整體拉出完畢
Timer1.Enabled:=FALSE;
end;
按下F9鍵運行程序,則窗口中的圖形將從左至右逐漸拉出,如圖20所示。
圖20 執行結果窗口
9. Delphi編寫打印程序的技巧有哪些
利用Delphi編寫打印程序,下面這些技巧的掌握將大有裨益。
n 獲取當前打印機的分辨率
Windows下的打印分辨率對打印程序有着至關重要的作用,若想獲取打印機的分辨率,請在程序中加入以下語句:
ShowMessage('水平分辨率'+inttostr(GetDeviceCaps(printer.Handle,LOGPIXELSX)) +chr(13)+'垂直分辨率:'+inttostr(GetDeviceCaps(printer.Handle,LOGPIXELSY)));
n 將結果直接送到打印機
Delphi提供了兩種打印方式,一是將結果輸送到窗體,再調用窗體的Print方法將結果輸送到打印機;二是將結果直接輸送到打印機。建議采用第二種方式。
n 盡量不要使用AssignPrn
盡管AssignPrn簡化了文本打印操作,使輸出到打印機象輸出到文件一樣簡單。但有時卻很不方便,例如用戶無法知道當前打印的行數,無法准確控制行距,無法靈活改變字體等。因此最好還是使用打印機的Canvas屬性執行打印操作。
n 用打印機的點數做度量單位
若想使打印程序在任何打印機上都能正常地打印,就必須改變度量單位。因為若采用固定的度量,不同分辨率的打印效果是不同的。此時最好使用打印機的點數做為度量單位。利用如下所示的代碼可以完成這個功能:
Var
PointX,PointY:integer;
Begin
PointX:=GetDeviceCaps(printer.Handle,LOGPIXELSX);
PointY:=GetDeviceCaps(printer.Handle,LOGPIXELSX);
printer.Canvas.rectangle(0,0,PointX*1,PointY*2)
end;
這樣無論使用何種打印機,都能得到一個1英寸寬、2英寸高的矩形。
n 添加打印程序單元
盡管Delphi在生成窗體時會自動在USES部分加入許多程序單元,但打印程序單元(Printers)卻沒有自動添加,因此需用戶手工添加該單元。
10. 如何防止多次執行程序
Windows下的很多程序可以多次執行,如“資源管理器”。但有時可能需要禁止程序副本的運行,即用戶多次執行程序時,只會激活那個已執行的程序,而不會啟動另一個副本。如何實現這一功能呢?可以在主窗體的OnCreate事件中添加如下所示的代碼。
Procedure TForm1.FormCreate(Sender: TObject);
var
ZAppName: array[0..127] of char;
Hold: String;
Found: HWND;
begin
Hold := Application.Title;
Application.Title := 'OnlyOne' + IntToStr(HInstance); //暫時修改窗口標題
StrPCopy(ZAppName, Hold); //原窗口標題
Found := FindWindow(nil, ZAppName); //查找窗口
Application.Title := Hold; //恢復窗口標題
if Found<>0 then begin
//若找到則激活已運行的程序並結束自身
ShowWindow(Found, SW_RESTORE);
Application.Terminate;
end;
end;
11. 如何獲得系統的內存信息
獲得系統的內存信息,需使用Windows API函數GlobalMemoryStatus,Delphi中的語法如下所示:
Procedure GlobalMemoryStatus(var lpBuffer: TMemoryStatus); stdcall;
其中的TMemoryStatus結構為:
TMemoryStatus=record
dwLength:DWORD;
dwMemoryLoad:DWORD;
dwTotalPhys:DWORD;
dwAvailPhys:DWORD;
dwTotalPageFile:DWORD;
dwAvailPageFile:DWORD;
dwTotalVirtual:DWORD;
dwAvailVirtual:DWORD;
下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Memo組件和1個Button組件,然后在Button的OnClick事件中添加如下所示的代碼。
Procedure TForm1.Button1Click(Sender: TObject);
var
MemoryStatus: TMemoryStatus;
begin
Memo1.Lines.Clear;
MemoryStatus.dwLength := SizeOf(MemoryStatus);
GlobalMemoryStatus(MemoryStatus);
with MemoryStatus do
begin
Memo1.Lines.Add(IntToStr(dwLength) +' Size of ''MemoryStatus'' record');
Memo1.Lines.Add(IntToStr(dwMemoryLoad) +'% memory in use');
Memo1.Lines.Add(IntToStr(dwTotalPhys) +' Total Physical Memory in bytes');
Memo1.Lines.Add(IntToStr(dwAvailPhys) +' Available Physical Memory in bytes');
Memo1.Lines.Add(IntToStr(dwTotalPageFile) +' Total Bytes of Paging File');
Memo1.Lines.Add(IntToStr(dwAvailPageFile) +' Available bytes in paging file');
Memo1.Lines.Add(IntToStr(dwTotalVirtual) +' User Bytes of Address space');
Memo1.Lines.Add(IntToStr(dwAvailVirtual) +' Available User bytes of address space');
end;
end;
按下F9鍵運行程序,單擊“內存信息”按鈕,則在Memo組件中將顯示有關內存的信息,如圖21所示。
圖21 執行結果窗口
12. 如何屏蔽Alt+Esc、Alt+Tab及Alt+F4鍵
為了禁止用戶在各個窗口間使用Alt+Esc或Alt+Tab鍵切換,可按以下步驟進行,首先設置窗體的FormStyle屬性為fsStayOnTop,WindowState屬性為wsMaximized。然后在窗體的OnCreate事件中添加如下所示的代碼,其目的是向Windows發送一個屏幕保護程序正在運行的消息。
Procedure TForm1.FormCreate(Sender: TObject);
var
temp: Integer;
begin
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 1, @temp, 0);
end;
在窗體的OnClose事件中添加如下所示的代碼,用以清除屏幕保護程序運行的標志。
Procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
temp: Integer;
begin
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 0, @temp, 0);
end;
而要實現屏幕關閉窗口的Alt+F4組合鍵,則只需在窗體的OnKeyPress事件中添加如下所示的代碼。
Procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (ssAlt in shift)and(key=115) then
key:=0;
end;
13. Delphi中的消息處理機制是怎樣的
在Delphi中可自定義消息,並可直接處理消息。對於那些希望在程序中截獲、過濾消息的用戶來說了解Delphi中消息處理的機制非常重要。
n Delphi VCL中消息的傳遞
Delphi中每一個VCL組件如Button,都有一內在的消息處理機制。它的特點是組件類接收到某些消息並把消息發送給適當的處理方法,如果沒有特定的處理方法,則調用缺省的消息處理句柄。其中mainwndproc是定義在Twincontrol類中的一個靜態方法,不能被重載(Override)使用。它不直接處理消息,而是交由wndproc方法處理,並為wndproc方法提供一個異常處理模塊。Mainwndproc方法聲明如下:
procedure MainWndProc(var Message: TMessage);
Wndproc是在TControl類中定義的一個虛擬方法,由它調用dispatch方法進行消息的分配,wndproc方法聲明如下:
procedure WndProc(var Message: TMessage); virtual;
而dispatch方法是在Tobject根類中定義的,其聲明如下:
procedure Tobject.dispatch(varMessage);
傳遞給dispatch的消息參數必須是一個記錄類型,而且這個記錄中第一個入點必須是一個cardinal類型的域(field),它包含了要分配的消息的消息號碼。例如:
type
Tmessage=record
Msg:cardinal;
wparam:word;
lparam:longint;.
result:longint;
end;
而Dispatch方法會根據消息號碼調用組件的最后代類中處理此消息的句柄方法。如果該組件和它的祖先類中都沒有對應此消息的處理句柄,Dispatch方法便會調用Defaulthandler方法。Defaulthandler方法是在TObject中定義的虛擬方法,其聲明如下:
procedure Defaulthandler(varMessage);virtual;
TObject類中的Defaulthandler方法只是實現簡單的返回而不對消息進行任何的處理。但可以通過對此虛擬方法的重載,在子類中實現對消息的缺省處理。對於VCL中的組件而言,其Defaulthandler方法會啟動windows API函數Defwindowproc對消息進行處理。
n Delphi中的消息處理句柄
在Delphi中用戶可以自定義消息及消息處理句柄。消息處理句柄的定義有如下幾個原則:
1. 消息處理句柄方法必須是一個過程,且只能傳遞一個TMessage型參數。
2. 方法聲明后要有一個Message命令,后接一個在0到32767之間的消息標號。
3. 消息處理句柄方法不需要用Override命令顯式指明重載祖先的一個消息處理句柄,另外它一般在組件的protected或private區聲明。
4. 在消息處理句柄中,一般先是用戶自己對消息的處理,最后用Inherited命令調用祖先類中對應此消息的處理句柄(有些情況下可能正相反)。由於可能對祖先類中對此消息的處理句柄的名稱和參數類型不清楚,所以調用命令Inherited可以避免這樣的麻煩,同樣如果祖先類中沒有對應此消息的處理句柄,Inherited就會自動調用Defaulthandler方法。
一般說來,消息處理句柄采用以下方法聲明:
procedure Mymsgmethod(varmessage:Tmessage); messageMsgtype;
同樣用戶也可以定義自己的消息,用戶自定義消息應從WM_USER開始。下面舉一個自定義消息和聲明消息處理句柄的例子:
constmy_paint=Wm_user+1;
type
Tmypaint=record
msgid:cardinal;
msize:word;
mcolor:longint;
msgresult:longint;
end;
type
Tmycontrol=class(TCustomControl)
protected
procedurechange(varmessage:Tmypaint);messagemy_paint;
……
end;
……
procedureTmycontrol.change(varmessage:Tmypaint);
begin
size:=message.msize; //設置組件尺寸屬性
color:=message.mcolor; //設置組件顏色屬性
……
inherited;
end;
n 過濾消息
過濾消息又稱消息陷阱。在某些情況下,用戶可能需要屏蔽某些消息,或者截獲某些消息進行處理。通過上述介紹,可見過濾消息一般有3種途徑:
(1) 重載組件繼承的虛擬方法wndproc;
(2) 針對某消息編寫消息處理句柄;
(3) 重載組件繼承的虛擬方法Defhandler,在其中對消息進行處理。
14. 如何實現文件關聯
建立文件關聯可實現某種擴展名的文件由對應的應用程序打開,而實現文件關聯的核心問題是對注冊表的操作。所有文件的關聯都保存在HKEY_CLASSES_ ROOT下,如圖22所示。
圖22 注冊表中的文件關聯鍵
因此要實現文件關聯必須在HKEY_CLASSES_ROOT 中增加兩個鍵值,一是和文件擴展名對應的類型說明;二是打開這種類型文件所需要執行的應用程序。下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體的OnCreate事件中添加如下所示的代碼。事實上,有關文件的關聯工作最好在主窗體的OnCreate事件中完成。
Procedure TForm1.FormCreate(Sender: TObject);
var
lphKey: HKEY;
sKeyName: string;
sKeyValue: string;
begin
sKeyName := 'myfile';
sKeyValue := '我的文檔';
RegCreateKey(HKEY_CLASSES_ROOT,pchar(sKeyName), lphKey);
RegSetValue(lphKey, '', REG_SZ,pchar(sKeyValue), 0);
sKeyName := '.qjg';
sKeyValue := 'myfile';
RegCreateKey(HKEY_CLASSES_ROOT,pchar(sKeyName), lphKey);
RegSetValue(lphKey, '', REG_SZ,pchar(sKeyValue), 0);
sKeyName := 'myfile';
sKeyValue := 'c:/Winnt/NotePad.exe %1';
RegCreateKey(HKEY_CLASSES_ROOT,pchar(sKeyName), lphKey);
RegSetValue(lphKey, 'shell/open/command', REG_SZ,pchar(sKeyValue), MAX_PATH);
end;
按下F9鍵運行程序,然后使用Regedit打開注冊表編輯器,可見新的鍵值已經建立,如圖23所示。
圖23 建立了新的鍵值
新建一個擴展名為qjg的文件,雙擊打開,則系統會自動使用Windows記事本程序打開這個文件。
15. 如何改變標題欄的字體
窗體標題欄的字體也可以修改的更加醒目,其方法是創建一個過程,截獲系統的WM_NCPAINT信息,然后使用畫布輸出標題的字體。下面完成一個示范程序,選擇“File|New Application”菜單命令,在窗體的單元文件中添加一個過程,如下所示。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
procedure WMNCPAint(var Mes : TWMNCPaint); message WM_NCPAINT;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.WMNCPAint(var Mes : TWMNCPaint);
var
ACanvas : TCanvas;
begin
ACanvas := TCanvas.Create;
try
ACanvas.Handle := GetWindowDC(Form1.Handle);
with ACanvas do begin
Brush.Color := clActiveCaption;
Font.Name := '黑體';
Font.Size := 16;
Font.Color := clCaptionText;
Font.Style := [fsBold];
TextOut(GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CXBORDER),
Round((GetSystemMetrics(SM_CYCAPTION) - Abs(Font.Height))/2) +1,
' 歡迎進入考勤管理系統!');
end;
finally
ReleaseDC(Form1.Handle, ACanvas.Handle);
ACanvas.Free;
end;
end;
end.
按下F9鍵,運行程序,則系統顯示的窗口如圖24所示。
圖24 執行結果窗口
16. 窗體生成時的事件次序是怎樣的
對於一般的SDI Form, 各事件的發生次序如下:
OnCreate
OnShow
在屏幕上顯示窗口
OnActivate
OnPaint
對於MDI窗口,而且MDIChild 的第一個子窗口是在程序啟動時,就出現在MDIForm中時,其各事件的發生次序如下:
主窗口的 OnCreate
子窗口的 OnCreate
子窗口的 OnShow
子窗口的 OnActivate
主窗口的 OnShow
在屏幕上顯示主窗口及第一個子窗口
主窗口的 OnPaint
17. 如何顯示密碼編輯框中的密碼
很多應用軟件中的密碼輸入框,用戶輸入的字符是以諸如“*”等符號屏蔽掉的。盡管如此,但其內部還是以當初的字符表示的,因此可以使用Windows API函數將其顯示出來。一般說來,Windows中的每一個窗口、組件都有自己 Name。而對於Form、Dialog Box及Message Box來說,其名稱就顯示在標題欄中;對於Edit、Button及Static Control,其名稱則顯示在本身占據的區域中。密碼編輯框本身就是個Edit組件,盡管顯示的是特殊字符,但其Name屬性不變。Windows提供了兩個API函數來獲得這個Name:
int GetWindowTextLength(HWND hWnd); //得到名稱的長度
int GetWindowText(HWND hWnd, LPTSTR lpString, int nMaxCount ); //得到名稱
其中參數lpString表示存放名稱的字符串地址,nMaxCount表示可復制的最大字符數。
下面完成一個示范程序,選擇“File|New Application”菜單命令,在默認的窗體中添加1個Label組件、1個Edit組件和1個Button組件,並將Edit1的PasswordChar屬性改為“*”,然后在Button1的OnClick事件中添加如下所示的代碼:
Procedure TForm1.Button1Click(Sender: TObject);
var
Name:PChar;
Long:integer;
begin
Long:=GetWindowTextLength(Edit1.handle)+1;
GetMem(Name,Long);
GetWindowText(Edit1.handle,Name,Long);
label1.Caption:=String(Name);
FreeMem(Name,0);
end;
按下F9鍵,運行程序,在編輯框中輸入字符,然后單擊“查看密碼”按鈕,則用戶輸入的字符會在Label中顯示出來,如圖25所示。
圖25執行結果窗口
18. 如何關閉Windows
可以使用API函數ExitWindowsEx控制Windows的開關,如關閉Windows,重新啟動Windows等。該函數的語法為:
ExitWindowsEx(UINT uFlags,DWORD dwReserved);
首先定義常數:
const
EWX_FORCE=4; //關閉所有程序並以其他用戶身份登錄
EWX_LOGOFF=0; //重新啟動計算機並切換到MS-DOS方式
EWX_REBOOT=2; //重新啟動計算機
EWX_SHUTDOWN=1; //關閉計算機
運行時給Off賦值,讓他等於EWX_SHUTDOWN或其他,然后調用以下語句即可實現關閉Windows。
ExitWindowsEx(off,0);
下面提供一個示范程序,其中所提供的AdjustToken過程主要用於在執行關閉Windows操作前判斷用戶的權限,即是否有權限關閉Windows,然后決定采用何種關閉方式。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure AdjustToken;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.AdjustToken();
var
hdlProcessHandle : Cardinal;
hdlTokenHandle : Cardinal;
tmpLuid : Int64;
tkpPrivilegeCount : Int64;
tkp : TOKEN_PRIVILEGES;
tkpNewButIgnored : TOKEN_PRIVILEGES;
lBufferNeeded : Cardinal;
Privilege : array[0..0] of _LUID_AND_ATTRIBUTES;
begin
hdlProcessHandle := GetCurrentProcess;
OpenProcessToken(hdlProcessHandle,
(TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY),
hdlTokenHandle);
LookupPrivilegeValue('', 'SeShutdownPrivilege', tmpLuid);
Privilege[0].Luid := tmpLuid;
Privilege[0].Attributes := SE_PRIVILEGE_ENABLED;
tkp.PrivilegeCount := 1; // One privilege to set
tkp.Privileges[0] := Privilege[0];
AdjustTokenPrivileges(hdlTokenHandle,
False,
tkp,
Sizeof(tkpNewButIgnored),
tkpNewButIgnored,
lBufferNeeded);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AdjustToken;
ExitWindowsEx((EWX_SHUTDOWN Or EWX_FORCE Or EWX_REBOOT), $FFFF);
end;
end.
按下F9鍵,運行程序,在系統顯示的窗口中單擊“關機”按鈕,即可關閉系統,如圖26所示。
圖26 執行結果窗口
小 結
本部分可作為Delphi系統開發的疑難手冊,它是很多程序設計人員心血的結晶。也許大科學家牛頓的話應該成為我們的座右銘“我之所以成功,是因為我站在巨人的肩膀上”。