承接前文,深入探討 CreateThread 入口函數的回傳值機制、GetExitCodeThread 取得結束代碼判斷執行緒是否結束,以及如何在執行緒中呼叫 VCL 物件方法。
CreateThread 的第三個參數 lpStartAddress 指向的函數原型:
function ThreadFunc(Parameter: Pointer): DWORD; stdcall;
Delphi System 單元定義 TThreadFunc = function(Parameter: Pointer): Integer,建議統一使用 DWORD 以符合 Win32 規範。
此回傳值會成為執行緒的 Exit Code。執行緒結束後,主執行緒可呼叫:
function GetExitCodeThread(hThread: THandle; var lpExitCode: DWORD): BOOL; stdcall;
lpExitCode = STILL_ACTIVE (259)。lpExitCode 即為入口函數的回傳值。入口函數不能直接是類別方法(方法指標包含隱藏的 Self,呼叫慣例不同)。解決方案:入口函數為全域函數,內部轉呼叫表單實例的方法。
type
TForm1 = class(TForm)
Button1, Button2: TButton;
procedure Button1Click(Sender: TObject); { 建立執行緒 }
procedure Button2Click(Sender: TObject); { 檢查結束代碼 }
private
procedure FormProc; { 供執行緒呼叫的方法 }
end;
var
hThread: THandle;
function MyThreadFun(p: Pointer): DWORD; stdcall;
begin
Form1.FormProc; { 呼叫表單方法 }
Result := 99; { 自訂結束代碼 }
end;
procedure TForm1.FormProc;
var
i: Integer;
begin
for i := 0 to 200000 do
begin
with Canvas do
begin
Lock;
TextOut(10, 10, IntToStr(i));
Unlock;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
ID: DWORD;
begin
hThread := CreateThread(nil, 0, @MyThreadFun, nil, 0, ID);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
ExitCode: DWORD;
begin
if hThread = 0 then
begin
Caption := '執行緒尚未啟動';
Exit;
end;
GetExitCodeThread(hThread, ExitCode);
if ExitCode = STILL_ACTIVE then
Caption := Format('執行緒執行中 (ExitCode=%d)', [ExitCode])
else
Caption := Format('執行緒已結束,結束代碼=%d', [ExitCode]);
end;

CreateThread 回傳的 hThread 需在適當時機 CloseHandle,否則核心物件洩漏。若使用 TThread 類別,FreeOnTerminate := True 會自動處理。TThread 或 TTask.Run (PPL),僅在需極致效能或整合舊 API 時使用 CreateThread。掌握 GetExitCodeThread 判斷執行緒狀態,結合全域函數轉呼叫物件方法,是純 API 多線程開發的核心技巧。理解這層機制,有助於除錯執行緒同步、資源釋序與異常處理等進階議題。