本文是為了加強記憶而寫,這里寫的大多數內容都是在編程的日常工作中使用頻率不高的東西,但是又十分重要。
---Murphy
1,構造和析構函數:
a,構造函數:
一般基於TComponent組件的派生類,都應該使用overload關鍵字進行繼承,Delphi中的對象沒有什么復合的概念,在設計時,從簡便的角度出發
一般都設計為耦合性較強,但是使用簡單的派生類即可。構造函數不是必寫的,除非“復合”這樣的對象實現,當省略構造函數時,會由其父類來實現
新對象的建立。下面是幾個常用的寫法:
constructor TfmBaseScreen.Create(AOwner: TComponent); //由於參數並沒有變化,所以這里在聲明的時候可以加override而不是overload
begin
inherited; // Create(AOwner) ; //這里的Create默認是可以省略的。
if AOwner is TPanel then
Self.Width :=TPanel(AOwner).Width;
end;
//-------------------------
constructor TfmHuaPianScan.Create(AOwner: TComponent; AQuery: TADOQuery;
AZDCode: string; AMvOutBillCode : string; MoveOutQuery : TADOQuery); //這個聲明時需要增加overload說明
begin
inherited Create(AOwner);
FQuery := AQuery; 這里實現的僅僅是私有變量的初始化,其實更多的應用是派生類成員對象的初始化,並需要在析構函數中進行釋放。
FZDCode := AZDCode;
FMvOutBillCode := AMvOutBillCode;
FMoveOutQuery := MoveOutQuery;
end;
//-------------------------
Constructor TDllLoader.Create(strDLLName : String); //這是一個基於TObject的類,由於沒有任何成員,所以這里Create什么關鍵字都不需要帶。
Begin
FhDLL := LoadLibrary(strDLLName);
ASSERT(FhDLL <> 0);
End;
b,析構函數
析構函數的使用比構造函數要嚴格一些,一定要在任何時候都使用override,以保證父類內存空間可以正確的釋放。
下面是幾個析構函數的寫法:
Destructor TDllLoader.Destroy(); //雖然函數體內沒有inherited但是聲明時一定要加上override.
Begin
If FhDLL <> 0 then
begin
FreeLibrary(FhDLL);
End;
end
//-----------------------------------------
destructor TfmTest.Destroy; //窗體類的析構,一定也要在聲明的時候增加override.
begin
temp.Free; //成員的釋放應該都安排在inherited之前。
inherited;
end;
c,窗體的創建和摧毀事件與構造析構的關系
當然,在窗體類的構造和析構當中,有很多成員管理方式,可以直接通過消息-->事件模式來實現,並不需要引用類及對象設計模式。
直接調用窗體的OnCreate和OnDestroy就可以完成很多事情。
我們先來看看窗體構造方法的一段代碼:
constructor TCustomForm.Create(AOwner: TComponent);
begin
GlobalNameSpace.BeginWrite;
try
CreateNew(AOwner);
if (ClassType <> TForm) and not (csDesigning in ComponentState) then
begin
Include(FFormState, fsCreating);
try
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
finally
Exclude(FFormState, fsCreating);
end;
if OldCreateOrder then DoCreate;
end;
finally
GlobalNameSpace.EndWrite;
end;
end;
procedure TCustomForm.AfterConstruction;
begin
if not OldCreateOrder then DoCreate;
if fsActivated in FFormState then
begin
Activate;
Exclude(FFormState, fsActivated);
end;
end;
從以上的VCL源碼可以看到Create構造方法和FormCreate 事件執行順序是可以調配的,當OldCreateOrder為True時,是執行Create時調用OnCreate事件的;
而OldCreateOrder為False時,是在窗體AfterConstruction中調用OnCreate事件的。
而OldCreateOrder屬性值默認就是False,也就是默認在AfterConstruction中調用。或者說,繼承窗口的時候,默認是先執行Create代碼段,再執行FormCreate代碼段。
我們再來看看Destroy和FormDestroy的執行順序,如下是VCL源碼:
procedure TCustomForm.BeforeDestruction;
begin
GlobalNameSpace.BeginWrite;
Destroying;
Screen.FSaveFocusedList.Remove(Self);
RemoveFixupReferences(Self, '');
if FOleForm <> nil then FOleForm.OnDestroy;
if FormStyle <> fsMDIChild then Hide;
if not OldCreateOrder then DoDestroy;
end;
destructor TCustomForm.Destroy;
begin
if not (csDestroying in ComponentState) then GlobalNameSpace.BeginWrite;
try
if OldCreateOrder then DoDestroy;
MergeMenu(False);
if HandleAllocated then DestroyWindowHandle;
Screen.RemoveForm(Self);
FCanvas.Free;
FIcon.Free;
FreeAndNil(FActionLists);
inherited Destroy;
finally
GlobalNameSpace.EndWrite;
end;
end;
同樣的析構方法跟FormDestroy事件的執行順序也是可以調配的,當OldCreateOrder為True時,系統是通過析構Destroy直接調用OnDestroy事件的;
而當OldCreateOrder為False時,系統是通過BeforeDestruction來調用OnDestroy事件的。
同Create的一樣,OldCreateOrder默認值是False,所以默認就是在BeforeDestruction中調用,也就是說,繼承窗體中,先執行OnDestroy事件代碼部分,再執行析構。
2,類方法及引用類
a,類方法
實現模式很簡單,只需要在定義過程或函數時,增加一個class關鍵字就可以了,它的地位跟C語言中的靜態方法的地位是一樣的。它所處理的信息都是與類相關,
不能引用對象成員。
下面是TObject中的一個標准的類方法,用來返回一個類名:
class function TObject.ClassName: ShortString;
b,類中類(引用類)
普通類的語法規則是:
類名=class(父類名)
成員描述;
end;
而類中類的語法是:
類名=Class of 父類名
成員描述;
end;
類中類的父類似乎是其一個對象成員,它可以直接調用其父類的類方法,而類中類定義出的對象,其實是一個類。
我們可以在用類中類定義的對象中,使用構造和析構方法,構造方法可以調用對象的Create也可以調用其類中類的Create,
但是,析構函數不能使用類中類的類方法,只能使用其創建對象的析構。這種方法常用於將類作為參數的使用上,方便
在某一個方法之中,對其不明類(但明其祖類)的對象,進行類方法調用。
下面是一個標准使用的例子,這是Vcl中TApplication所帶的方法,雖然名稱是創建Form,其實對於任何基於TComponent的
對象都是可以創建的,其實現手法就是類中類:
procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
Instance: TComponent;
begin
// Set flag that TCustomForm constructor can read, so it knows if it's being
// created as a main form or not (required when MainFormOnTaskbar is True)
FCreatingMainForm := (FMainForm = nil) and InstanceClass.InheritsFrom(TForm);
Instance := nil;
try
{$IF DEFINED(CLR)}
Instance := InstanceClass.Create(Self);
Reference := Instance;
{$ELSE}
Instance := TComponent(InstanceClass.NewInstance); //這里是直接用的TComponent對象賦值一個派生類空間,面向對象是允許的
TComponent(Reference) := Instance; //這里的Reference是一個實參,這句用於指向創建的對象
try
Instance.Create(Self); //NewInstance創建的空間只是分配一片空白區域,這里將其頭部區域格式化成TComponent
except
TComponent(Reference) := nil;
raise;
end;
{$IFEND}
if (FMainForm = nil) and (Instance is TForm) then //當系統中還沒有明確主窗口時,而創建的對象是窗體對象,則在這里將其自動設置為主窗體。
begin
TForm(Instance).HandleNeeded;
FMainForm := TForm(Instance);
if MainFormOnTaskBar then
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_NOACTIVATE);
ChangeAppWindow(Handle, not MainFormOnTaskBar, not MainFormOnTaskBar);
end;
finally
if (FMainForm = nil) and (Instance is TForm) then
TForm(Instance).FCreatingMainForm := False;
end;
end;
類方法中的Self,指的是類本身,而不是對象,所以沒辦法象普通Self那樣使用。
一個類必須創建具有其對象狀態的成員,才是有意義的類。在我上一家公司中,見到有人賣弄高深的把全局方法都建立在一個類中,用類實現。
這樣的類,既沒有其多樣的對象,在方法實現時,甚至一點與類相關的東西都沒有,還不如建立一個公共方法單元。
一個標准的類,也不能僅僅是對一堆封裝成員的Get和Set操作,應該在成員的邏輯處理中,簡化其應有的方法,又要保留必要的變化細節。
否則的話,還不如定義一個結構體。
3,構造自己的異常,及合理應用
EFileOpenFailed = class(Exception)
public procedure Warning(); virtual; abstract; //這里是純虛函數的定義,在使用時,還必須再定義一層子異常類。
end;
所有異常類,都應該以E開頭,並且從Exception繼承。並且,在觸發異常時,應該從繼承樹的枝節點寫起,
根節點的異常觸發應該寫到最后。異常的調用,可以在except包含段中以
[on 異常實例:異常類定義 do]的模式進行調用。再或者,可以用[raise 異常類名.Create(構造參數列表);]來直接拋出異常
try
SimulateError(Button)
except
on E : EFileOpenFailed do
E.Warning();
on E : Exception do
ShowMessage(E.Message);
end;
4,起源於萬物之王的消息機制
Delphi跟Windows消息是一脈相承的,或者說是擴展消息。Windows消息到了Delphi被重新轉換成固定的結構體(Record),
然后,由Delphi的對象進行分發處理,再由消息函數進行接收運行其對應功能。
對於Delphi,可以說所有的對象都具有消息接收功能,這個需要追溯到對象的最初定義TObject:
TObject = class
public
constructor Create; //構造函數
procedure Free; //用於釋放對象
class function InitInstance(Instance: Pointer): TObject; //使用固定地址,格式化一片實例數據,並返回其對象指針。但是這個並不能分配內存,只操作已分配的內存
procedure CleanupInstance; //清空對象本身所指向的區域內存數據,但是並不釋放內存
function ClassType: TClass; inline; //返回類類型,這里的inline相當於注釋,表示該功能向下兼容,並且可以減少編譯文件的大小。
class function ClassName: string; //返回類名
class function ClassNameIs(const Name: string): Boolean; //用類名判斷類
class function ClassParent: TClass; //返回父類,如果強制返回一個基於TObject的父類,則會報錯。
class function ClassInfo: Pointer; inline; //返回類信息的指針
class function InstanceSize: Longint; inline; //返回基於當前類的實例使用的空間大小
class function InheritsFrom(AClass: TClass): Boolean; //判斷是否繼承於特定類
class function MethodAddress(const Name: ShortString): Pointer; overload; //根據類所含方法的名稱,返回對應指針。
class function MethodAddress(const Name: string): Pointer; overload; //同上
class function MethodName(Address: Pointer): string; //根據方法的地址指針,返回方法名
function FieldAddress(const Name: ShortString): Pointer; overload; //根據屬性的名稱,返回屬性的地址。
function FieldAddress(const Name: string): Pointer; overload; //同上
function GetInterface(const IID: TGUID; out Obj): Boolean; //獲得一個接口地址,返回執行成功與否
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; //直接返回一個接口地址
class function GetInterfaceTable: PInterfaceTable; //獲取接口表地址
class function UnitName: string; //類定義所在的單元文件名
function Equals(Obj: TObject): Boolean; virtual; //對象比較
function GetHashCode: Integer; virtual; //得到哈希代碼
function ToString: string; virtual; //等同ClassName,可多態改動
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer): HResult; virtual; //以安全模式調用異常
procedure AfterConstruction; virtual; //構造后處理
procedure BeforeDestruction; virtual; //析構前預處理
procedure Dispatch(var Message); virtual; //消息分發
procedure DefaultHandler(var Message); virtual; //默認接收方法,可以接收所有編號的消息。
class function NewInstance: TObject; virtual; //開辟一個新對象空間,其地址是由系統自動分配的
procedure FreeInstance; virtual; //釋放實例空間
destructor Destroy; virtual; //析構
end;
消息使用的三個步驟,
<1>,給具有消息結構體的消息變量賦值。
<2>,構造一個具有處理消息方法的類。處理消息方法的書寫格式:方法名(var 消息參數); message 消息號;
<3>,用一個消息處理類的實例,用Dispatch或者DefaultHandler方法對消息進行分發和處理。這里的分發是指分發到類內部過程。
例程:
TMyMsg = record //消息定義,這里是一種最簡單的消息結構體。
Msg : Cardinal;
MsgText : ShortString;
end;
TMsgAccepter = class //接收消息類定義,這里省略了消息方法闡述部分
private
procedure AcceptMsg2000(var msg : TMyMsg); message 2000; //這里定義觸發方式及觸發消息號
procedure AcceptMsg2002(var msg : TMyMsg); message 2002;
public procedure
DefaultHandler(var Message); override; //這里用於接收其他編號的消息
end;
//調用過程
var
MsgAccept : TMsgAccepter;
Msg :TMyMsg;
begin
MsgAccept := TMsgAccepter.Create;
try
Msg.Msg:=2000;
Msg.MsgText:='消息測試文本';
MsgAccept.Dispatch(Msg); //TObject自帶的方法
finally
MsgAccept.Free;
end;
end;
VCL的事件定義都是如此,先在VCL控件中定義出接收特定消息的方法,再由事件屬性(這里的屬性是一個指針),去指定相關方法,
並在事件(TNotifyEvent)子類中形成用戶事件區。
注意:以WM_開頭的消息,是Windows定義的消息;以CM_開頭的消息,是VCL庫自定義的消息。
5,行標的定義Label.
面向對象的編程中並不建議使用行表和goto跳轉語句,所以在通常的Delphi代碼中很少見,但是並不是說Delphi不具備goto跳轉,eg.
var
a,b: Integer;
label
X,Y;
begin
if a > b then
goto X
else
goto Y;
X:
WriteLn('a>b');
Y:
WriteLn('b>a');
end;
6,一個幾乎脫離VCL的程序框架:
program Console;
{$APPTYPE CONSOLE} //這里表示支持對用戶控制台的操作
//uses SysUtils;
var
str:string;
begin
writeln('您好,這是一個示范程序,請輸入一行文字:');
readln(str);
writeln('您輸入的是:',str);
Writeln(TObject.classname); //這行代碼僅僅表示,可以訪問VCL中的System.pas
if TObject.ClassNameIs('TObject') then
Writeln('OK');
readln;
end.
7,回調函數與方法指針:
回調函數是引用C語言中的叫法,在Delphi中,既可以是函數,又可以是過程。
a,簡單的函數指針
<1>定義語法:
type
過程類名=procedure ([參數表列]); //注意,這里procedure后沒有過程名
函數類名=function([參數表列]) : 返回類型;
end;
<2>用以上定義的方法類可以定義出一個方法指針變量,並且這個變量可以作為其他方法的參數來使用.
回調函數的形參實現,以下是闡述部分事例:
procedure 回調函數名(..., A : 過程類名,...);
begin
...A([參數表列]).. //這里實現了對過程A的使用,使用時不需要考慮A的闡述部分,因為它還沒有真正的實體,但是參數表列要跟<1>中定義的一致。
end;
<3>主調函數的使用.
主調函數中,需要定義出一個具有實體的方法,這個方法作為參數使用,替換<2>中的A成為實參。
這個作為實參出現的方法不需要額外的說明其類型,但是必須要與<1>中的參數表列及返回類型一致。
eg.
function 作為實參的方法([參數表列]) : 返回值;
begin
...... //注意,這一部分才是真正的實現了<1>中方法的實體
end;
procedure 一個主調的方法;
begin
回調函數名(..., 作為實參的方法,...); //注意,這里的<2>中的A已經被換成了上面的實體方法。
end;
整個回調函數的實現,是分三步的,前兩步分別是定義〖方法指針〗及〖回調函數與方法指針的使用關系〗,第三步
才是真正實現〖方法指針的實體及回調函數的使用〗。
b,與類和對象相關的方法指針:
語法定義跟簡單方法指針有點象,不過結尾多了 of object:
過程類名=procedure ([參數表列]) of object;
函數類名=function([參數表列]) : 返回類型 of object;
這類的指針往往是指向一個對象方法,甚至類方法(作為類方法指針,前面要加class)來用。
其指針函數在實際實現時,其實是隱藏了一個self參數的,可以針對對象或者類來進行操作。並且,以of object定義的
指針,不可以與簡單方法指針轉換和賦值。
這也就是我們為什么在窗體中這么寫代碼會報錯:
button1.onclick=buttonclick; //這里buttonclick雖然單獨執行與button1.onclick可能是一樣的,但是它僅僅是個本地過程,而onclick是個對象事件。
我們再來看看事件的標准定義:
TNotifyEvent = procedure(Sender: TObject) of object; //這句話其實就是定義了一個方法指針,而OnClick就是這種方法實例。
c,跨工程接口調用的關鍵字_cdecl, _stdcall,_fastcall,_cdeclspec
這節內容實際是與方法指針沒有直接關系的,但是一般使用方法指針時又極易涉及到這些關鍵字,所以放在一起講了。
這個下划線是C語言約定的標准,對於不同的語言其使用的方式也不一樣。
cdecl 是C Declare 即C語言標准接口。
stdcall 是 standard Call ,這個接口標准被Win API 所采用,也是最為通用的調用標准,例如delphi也用它。
fastcall 在C++Builder當中使用廣泛。
cdeclspc 用的比較少,它的直接意思是一個dll引用方法。
所有的關鍵字與其定義接口的關鍵字必須一致,其實際意義是這些方法及方法參數的壓棧方法,細節我在這里不詳述。
下面是一個標准的Delphi外部方法接口的定義:
{要注意,加了關鍵字的方法名都是區分大小寫的。這里的MTASDK就是外部方法名。}
function mta001_Exit(): Integer; stdcall; external MTASDK name '_mta001_Exit';
最后再說明一點,假如定義了一個方法指針變量A,那么@A並不代表A的指針,而且是一個標准類型4字節的Pointer指針。
只有再多加一個@才能代表其方法地址,即:@@A
8,數組,集合與類型別名的使用。
a,數組定義:
var
數組變量 : array [數組起始下標..終止下標] of 元素類型;
允許定義復雜點的數組,多維數組及數組的數組。
數組變量 : array [起始下標1..終止下標1,起始下標2..終止下標2] of 元素類型; //這是一個二維數組.
數組變量 : array [起始下標..終止下標] of array [起始下標..終止下標] of 元素類型。 //這是一個數組的數組。
數組允許在定義變量時省略下標,然后在使用時,再用SetLength來設定數組的長度(即開辟內存),這樣定義的數組成為動態數組。
而數組名不帶下標時,僅僅等同於一個指針。動態數組的默認起始下標是從0開始的整數。
數組可以通過Low(數組名)和High(數組名)來返回數組的上下標。
b,集合定義:
type
集合名=set of [元素表列]; //元素表列可以是多個元素用逗號隔開,也可以用上下標加..表示,還可以是枚舉類型名。
TToolButton = (tbAdd, tbDel, tbEdit, tbPost, tbCancel); //這是一個枚舉
TToolBtnSet = set of TToolButton; //這是一個集合
這個也可以在定義變量的時候直接定義集合:
var
集合變量 : 集合類型=set of [元素表列];
常用的集合運算有in ,並且可以用+/-[元素表列]來增刪集合中的元素。
注意:集合的類型實例數是有限制的,必須在255以內。例如:集合類型=set of Char; 這樣的定義是合法的,因為Char的實例數剛好255,
而:集合類型=set of string; 或者:集合類型=set of integer; 這樣的定義是會報系統錯誤的。 所以,一般集合引用枚舉類型來定義很常見。
c,類型別名:
類型別名 : type 原類型名;
注意,這樣定義的變量,在計算和賦值是是完全兼容的,但是在out型參數使用時,不匹配是會報錯的。
類型別名的定義,有利於代碼的優化和變更。
9,多線程的使用
一個程序就是一個進程,而一個進程中是擁有一個主線程的,我們經常需要設計各種過程的並行執行,這就需要用到線程設計。Delphi中使用線程的方式一般有三種:
標准的TThread類創建線程對象模式;直接使用WinApi調用模式;偽線程Application.ProcessMessages模式。
而在線程使用中,最關鍵的處理核心就在於資源共享,資源使用中,一般是通過資源引用標記來使得其安全釋放的,不幸的是,這些資源標記大多時候是需要我們自己來建立和標記的。而且VCL中,可供用戶操作的繼承於TControl類都不是線程安全的,並且部分非用戶界面的VCL類對象也不是線程安全的(例如TList),VCL中有很都針對多線程設計的
安全類都以Thread前綴來標明(例如TThreadList)。所以,我們盡量不要在進程的excute事件中處理VCL對象,如果非要這么做不可,可以仿照Objective-C中的對象引用計數機制。
好了,現在就針對線程的三種模式一一說明:
a,標准的TThread類(這里有我一篇完整的例子分享一個多線程實現[冒泡][選擇][二分法]排序的例子)
TThread類,因為含有一個純虛方法excute,所以它是一個抽象類,不允許直接建立對象來使用。我們使用它時,必須建立一個非抽象的派生類,再進行使用。
TThread在Delphi中是可以作為一個單元獨立定義的,我們可以從〖新建〗--->〖Delphi Files〗--->〖Thread Object〗來建立一個獨立的線程單元。
TThread類的聲名如下:(這里只揀主要屬性和方法進行說明)
TThread = class
private
...
protected
procedure DoTerminate; virtual; //線程結束時執行
procedure Execute; virtual; abstract; //線程的主實現過程。執行完畢自動設置Terminated為True,並調用事件OnTerminate
procedure Synchronize(Method: TThreadMethod); //將線程中的一個對象過程,置於程序中的主線程中同步執行,一般被excute中調用。
property ReturnValue: Integer read FReturnValue write FReturnValue; //可作為函數ThreadProc的返回值
property Terminated: Boolean read FTerminated; //線程結束標記,觸發OnTerminate后自動置True
public
constructor Create(CreateSuspended: Boolean); //構造方法,參數如果為False,則在創建線程后自動執行Excute;如果為True,則掛起
destructor Destroy; override;
procedure AfterConstruction; override;
procedure Resume; //喚醒掛起的線程
procedure Suspend; //掛起線程
procedure Terminate; //中斷線程
function WaitFor: LongWord;
property FatalException: TObject read FFatalException;
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate; //線程在結束時是否需要自動釋放
property Handle: THandle read FHandle; //線程句柄
{$IFDEF MSWINDOWS}
property Priority: TThreadPriority read GetPriority write SetPriority; //是一個枚舉類型,標記着該線程的優先級
{$ENDIF}
...
property Suspended: Boolean read FSuspended write SetSuspended; //線程是不是掛起狀態
{$IFDEF MSWINDOWS}
property ThreadID: THandle read FThreadID; //線程ID,注意:一個線程是可以有多個句柄的,但是線程ID是唯一的。
{$ENDIF}
...
//線程的結束事件。默認調用DoTerminate過程,也可以自定義一個事件方法然后用OnTerminate進行指向。
property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
end;
假如我們已經派生了一個事件類TMyThread,並實化了Excute方法,那么我們就可以這么產生一個線程
var
MyThread1 : TMyThread;
....
MyThread1 := TMyThread.Create(True); //這個線程是掛起的,如果參數是False,其實MyThread1變量都可以省了。
MyThread1.OnTerminate := 回調函數;
MyThread1.FreeOnTerminate := True; //其實下面這兩句也可以在類TMyThread聲明中,用構造函數直接設置死。
MyThread1.Resume;
這樣就可以完成運行一個線程了,只不過對於那些非線程安全的對象處理,我們需要在TMyThread的類聲明中,用一個對象方法進行處理。
例如在TMyThread中,有這樣一個方法:
protected
procedure OperatorVCL;
...
我們可以在Excute實化時這么處理:
procedure TMyThread.Excute;
begin
...
Synchronize(OperatorVCL); //回調處理
end;
我們可以這么理解TThread的設計,當一個程序中出現一個任何一個附屬線程的時候,程序會自動建立一個0X0的隱藏窗口,用來接收線程的Synchronize方法。並且,所有的附加線程,都是共用這個隱藏窗口的,並且在傳遞Synchronize方法時,將其對應的線程Self作為一個IParam傳給主線程。
b,跳過Thread類,直接用Win Api模式來實現一個進程:
//定義一個線程句柄
var
hThread: THandle;
//定義一個線程的主方法
function MainThreadMethod( AParam : integer ) : boolean; stdcall; //這里是過程,函數都可以,參數返回值也可以隨便定義,但是stdcall關鍵字不能少。
begin
...
end;
//直接創建線程,並喚醒或掛起
var
ID: DWORD;
...
hThread := CreateThread(nil, 0, @MainThreadMethod, nil, CREATE_SUSPENDED, ID);
ResumeThread(hThread);
SuspendThread(hThread);
...
這個事例中用CreateThread,ResumeThread,SuspendThread三個方法直接用API函數完成了對一個線程的使用。
當然,還有一系列的線程方法,例如:TerminateThread,ExitThread,GetCurrentThread: THandle;GetCurrentThreadId: DWORD; 等等。
其實,用TThread類實現多線程時,也調用CreateThread和ExitThread方法,不過Delphi重新定義了一個線程方法:BeginThread(非對象或類方法哦),
這個方法將CreateThread系列方法打包簡化了,並且在Thread的構造函數中默認調用了BeginThread方法。
注意:API系列的Thread方法是在Windows單元定義的;BeginThread方法是在System單元定義的;而TThread的類定義,是在Classes單元定義的。
c,偽線程操作Application.ProcessMessages
這個方法一般是用在循環當中,其作用是防止在一個事件中執行循環的時候,把主線程完全占有,這時候窗體操作UI處於死鎖狀態,即不能停止,也不能點擊或拖動。
而增加了Application.ProcessMessages,就可以讓程序執行到這里響應一個Windows的標准消息,這種〖分步〗執行的效果也跟多線程類似。
我們可以在系統響應Application.ProcessMessages的消息的時候,配合Sleep語句,並改變一個控件內容,從而達到控制循環條件的效果。
但是有一點一定要注意:Application.ProcessMessages 一定不能在OnTimer事件中使用,否則會造成消息迭代,主線程阻塞。雖然OnTimer的機制跟線程非常
相似,但是其實它是基於主線程實現的,任務在這里阻塞一樣會影響到主線程的運行。