首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 動態連結庫程式設計:變數管理、錯誤處理與初始化機制詳解

     編輯:Delphi

DLL 中的變數與記憶體段管理

動態連結庫(DLL)擁有獨立的資料段(DS),這意味著 DLL 內部宣告的所有變數都是私有的。呼叫模組無法直接存取 DLL 定義的變數,必須透過函數或程序介面進行資料交換。同理,DLL 也無法直接使用呼叫者模組中宣告的變數。

關於堆疊段(SS),DLL 不擁有專屬的堆疊段,而是共用呼叫應用程式的堆疊。因此,在 DLL 的程序或函數中,絕不可假設 DS 等於 SS。某些語言在小型記憶體模型編譯下會有此假設,但 Delphi 從不產生假設 DS = SS 的程式碼,其執行階段庫也避免此類假設。若開發者需內嵌組合語言程式碼,務必避免將 SS 和 DS 登錄器設為相同值。

DLL 中的執行階段錯誤處理

由於 DLL 無法控制應用程式的執行流程,異常處理相對困難。編寫 DLL 時必須格外謹慎,確保被呼叫時能正常執行。當 DLL 發生執行階段錯誤時,DLL 不一定會從記憶體卸載(因為其他應用程式可能正在使用它),但呼叫 DLL 的程式會異常終止。這會導致一個問題:若 DLL 已修改並重新呼叫,記憶體中殘留的可能仍是舊版本,修改後的程式碼未獲驗證。

解決方案有二:

  1. 在程式的異常處理區塊中,顯式將 DLL 從記憶體卸載;
  2. 完全退出 Windows 並重新啟動,再執行相應程式。

與一般應用程式相比,DLL 的執行階段錯誤處理更具挑戰性,後果也更嚴重,因此程式設計師在編碼時必須充分考慮各種異常情況。

DLL 初始化程式碼的撰寫

傳統 Windows DLL 程式設計需要兩個標準函數:LibMain(啟動初始化)和 WEP(卸載前清理)。Delphi 以專屬方式實現這兩個功能——在專案檔的 begin...end 區塊中撰寫初始化程式碼。其主要特色:

  1. 初始化程式碼為可選項,系統會自動完成必要工作(如解鎖資料段);
  2. 可設定多個結束程序,依序呼叫;
  3. LibMain 和 WEP 對使用者透明,由系統自動調用。

初始化程式碼主要工作:初始化變數、配置全域記憶體區塊、註冊視窗物件等。結束程序透過預定義變數 ExitProc 實現,系統自動調用 WEP,依序執行 ExitProc 指向的程序,直到其為 nil。

範例:正確設定結束程序

library Test;
{$S-}
uses WinTypes, WinProcs;
var
  SaveExit: Pointer;
procedure LibExit; far;
begin
  if ExitCode = wep_System_Exit then
  begin
    { 系統關閉時的處理 }
  end
  else
  begin
    { DLL 卸載時的處理 }
  end;
  ExitProc := SaveExit; { 恢復原結束程序指標 }
end;
begin
  { DLL 初始化工作 }
  SaveExit := ExitProc; { 保存原結束程序指標 }
  ExitProc := @LibExit; { 安裝新結束程序 }
end.

初始化時先保存原 ExitProc,再指向新程序;結束時恢復原值。ExitCode 變數標示結束狀態:WEP_System_Exit(Windows 關閉)或 WEP_Free_DLL(DLL 卸載)。編譯結束程序時必須關閉堆疊檢查({$S-})。

一般 DLL 撰寫範例:字串搜尋函數

以下範例將字串操作函數封裝於 DLL,供其他程式呼叫。為確保跨語言相容性,參數採用無結束符的字元陣列(PChar),而非 Object Pascal 的 String 型別。

library Example;
uses SysUtils, Classes;
{ 回傳字元在字串中的位置 }
function InStr(SourceStr: PChar; Ch: Char): Integer; export;
var
  Len, i: Integer;
begin
  Len := StrLen(SourceStr);
  for i := 0 to Len - 1 do
    if SourceStr[i] = Ch then
    begin
      Result := i;
      Exit;
    end;
  Result := -1;
end;
exports
  InStr index 1 name 'MyInStr' resident;
begin
end.

exports 子句指定匯出名稱(MyInStr)、索引號(1)與常駐屬性(resident),加快呼叫速度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved