DLL 專案結構
testDll.dpr -- 專案檔,exports 匯出函數
DLLFrm.pas/dfm -- 表單單元,含 TMonthCalendar 日期選擇元件
FtestDll.pas/dfm -- 測試主程式,動態/靜態載入示範
核心程式碼解析
1. DLL 專案檔
library testDll;
uses SysUtils, Classes, DLLFrm in 'DLLFrm.pas' {frmDLL};
{$R *.res}
exports
ShowDLLModalForm, ShowDLLForm, ShowCalendar; -- 標準呼叫慣例
begin
end.
2. 表單單元 - 匯出函數定義
procedure ShowDLLModalForm(aHandle: THandle); stdcall;
procedure ShowDLLForm(aHandle: THandle); stdcall;
function ShowCalendar(AHandle: THandle; ACaption: String): TDateTime; stdcall;
3. 實作細節
- 模態顯示:
Application.Handle := aHandle; Form.ShowModal; 確保父視窗停用
- 非模態顯示:
Form.Show; 需自行管理表單釋放
- 日期回傳:
ShowCalendar 依 ModalResult 回傳選取日期或預設值
- 記憶體管理:
ShareMem 機制(若傳遞 String 需加入),範例採用 PChar/ShortString 避免依賴 BORLNDMM.DLL
4. 測試程式 - 動態載入
// FormCreate
DLLHandle := LoadLibrary('testDll.dll');
if DLLHandle = 0 then RaiseLastOSError;
// 按鈕點擊 - 取得函數位址並呼叫
@ShowDLLForm := GetProcAddress(DLLHandle, 'ShowDLLModalForm');
if @ShowDLLForm = nil then RaiseLastOSError;
ShowDLLForm(Application.Handle);
// FormDestroy
if DLLHandle <> 0 then FreeLibrary(DLLHandle);
5. 靜態載入替代方案
// 宣告外部函數
procedure ShowDLLModalForm(aHandle: THandle); stdcall external 'testDll.dll';
// 直接呼叫
ShowDLLModalForm(Application.Handle);
關鍵最佳實務
- stdcall:WinAPI 標準呼叫慣例,確保跨語言相容
- Application.Handle 傳遞:DLL 內
Application.Handle := aHandle 關鍵,否則模態視窗不會正確停用父視窗
- 例外處理:DLL 內部例外不跨越邊界,需自行
try...except 包裹並回傳錯誤碼
- 資源釋放:
FormDestroy 必呼叫 FreeLibrary,避免模組洩漏
- 字串傳遞:優先使用
PAnsiChar/PWideChar,避免 ShareMem 部署複雜度
常見坑點
- DLL 與 EXE 使用不同版本的 RTL/VCL → 記憶體管理衝突
- 未正確傳遞
Application.Handle 導致模態視窗行為異常
- 動態載入未檢查
GetProcAddress 回傳值直接呼叫 → AV