本文用通俗易懂的語言介紹Linux平台上共享對象庫(SO)的基本概念及主要優點
通過剖析在Delphi for Linux中應用SO與在Delphi for Windows中應用DLL的異同
以編程實例講述了Linux平台的SO庫文件的組成
SO庫文件的函數重載
特殊編譯指令
采用Delphi for Linux創建SO的編程規則
使用前的Linux系統設置
以及在Delphi for Linux中用隱式或顯式鏈接方法裝入和使用SO函數的基本方法
經驗及技巧
並對應用SO可能出現的問題進行了探討和分析
一共享對象庫基本概念
Delphi for Linux是Borland公司推出的基於Linux平台的面向對象的可視化開發工具是目前Linux平台上很好的應用開發工具Delphi for Linux也稱Kylix大家用Kylix開發Linux應用程序時可能使用過Linux操作系統本身帶的大量SO文件SO是一種特殊的運行文件包含若干方法對象和資源它不能直接運行但可以被Kylix應用程序或其它可執行文件動態調用SO文件擴展名為so編譯前源文件擴展名為dpr本文所舉例子均在Red Hat Linux 及Kylix 環境下調試編譯通過並可正常運行
圖是Kylix主程序與SO庫的層次關系圖從中可看出使用SO庫有以下幾個優點
圖
Kylix主程序與SO庫的層次關系圖
◆ 多個Kylix程序或它的多個單元文件可通過接口共用一個SO庫文件另一方面某一個Kylix程序可通過多個接口使用多個SO庫文件這樣SO變成一種可共用的資源實現真正的資源共享大大縮小了Kylix應用程序的執行代碼增強了軟件的可重用性
◆ 將SO文件作為Kylix應用程序的公共調用模塊設計時由於其獨立於應用程序軟件升級時只需修改SO庫文件及編譯SO無需更改及重編譯Kylix應用主程序
◆ 不僅可使用Kylix編寫SO庫還可使用C或C++等常用語言來編寫只要遵循特定的接口規范
二共享對象庫的創建
SO庫文件的構成
SO庫文件和Kylix標准單元文件的內部結構基本相同也有聲明實現及初始化部分區別之一在於SO庫只是其它程序可以調用的方法(包括函數及過程)集合區別之二庫程序以library關鍵字而非project開頭啟動其項目文件庫程序包含有exports語句其列出要向外部提供的導出函數及過程下面是SO庫文件代碼的簡單例子用以說明其構成
library MyFirstSO
uses
SysUtils classes { Delphi for Windows 中引用類庫為Windows }
function Add (ACharBChar)Integercdecloverload
begin
Result := Ord (A) + Ord (B)
end
function Add (AIntegerBInteger)Integercdecloverload
begin
Result := A + B
end
function Double (NInteger)Integercdecl
begin
Result := N *
end;
exports
Add (AIntegerBInteger)
Add (ACharBChar) name AddChar
Double
SO庫文件中的函數重載
SO庫也可以使用重載函數(即多個函數使用相同名稱不同參數)使用時需在重載的函數聲明後標上overload指令Kylix可以用原名稱導出一個重載函數在exports從句中表示其參數表若要導出多個重載函數則要在exports從句中用name字句指定不同名稱以區別重載這可從上面的例子MyFirstSO中看出Add是重載函數為調用時區分一個用原函數聲明Add導出另一個用AddChar導出
SO庫的特殊編譯指令
編譯後生成的SO庫運行文件使用lib前綴和so擴展名考慮到實際命名規則與版本和支持符號鏈Kylix在Object Pascal語言中引入了幾個特殊編譯指令這些在Delphi中沒有什麼意義庫源文件MyFirstSOdpr編譯後產生的執行文件為libMyFirstSOso
◆ $SOPREFIX 改變名稱前綴默認為lib(正常庫)或bpl(Kylix包)用前綴區別兩種庫是因為Linux的庫用單一擴展(so)
◆ $SOSUFFIX 在庫名與擴展名之間增加文本指定版本或其它信息
◆ $SOVERSION 在擴展名之後增加版本號
◆ $SONAME 表示相關符號鏈名由編譯器自動生成
例如下列代碼生成庫libsimpleso和符號鏈libsimpleso
library simple
uses
SysUtilsClasses
//函數定義省略
{$SOVERSION }
{$SONAME libsimpleso}
三共享對象庫的使用
Kylix應用程序使用SO庫時可以采用兩種方式一種是隱式鏈接(Implicit linking)也稱靜態裝入另一種是顯式鏈接(Explicit Linking)也稱動態裝入下面分別介紹這兩種鏈接方式的使用方法技巧及將窗體對象放入SO庫的技術
使用前的系統設置
自定義SO庫建好後Kylix應用程序調用時會報錯這是因為Kylix找不到新建庫必須對系統進行相關設置這與在Delphi for Windows中使用DLL庫不同DLL庫建好後只需將編譯後的DLL文件放到Delphi主程序目錄下即可使用操作步驟如下
◆ 將編譯好的SO庫文件放到Linux系統庫目錄/lib或/usr/lib下或者在Linux系統庫路徑shell變量LD_LIBRARY_PATH中加入自定義SO庫文件所在路徑
◆ 在根用戶(root)下用ldconfig命令刷新庫緩沖區
◆ 對Kylix執行文件使用ldd命令查看該程序所關聯的SO庫
隱式鏈接
隱式鏈接是指在應用程序開始執行時就將SO庫文件加載到應用程序中實現隱式鏈接並不難只需在應用程序中加入庫函數的聲明語句及庫的external定義從句則庫函數可以和一般局部函數一樣使用比如要使用libMyFirstSOso中的Add函數則只要在應用程序中增加下面語句
function Add (AIntegerBInteger)Integercdecl
external libMyFirstSOso
顯式鏈接
顯式鏈接是應用程序在執行過程中可根據實際需要隨時加載SO庫文件也可以隨時卸載SO庫文件還可在運行時進行SO庫的切換而這些是隱式鏈接無法做到的與隱式鏈接相比顯式鏈接具有更大的靈活性
在Kylix中要動態裝入庫和調用導出函數可以用Delphi仿真代碼或自然Linux方法下面分別介紹這兩種方法
()用Delphi仿真代碼動態裝入
在Windows中動態裝入DLL是用Windows API函數—LoadLibrary或Delphi提供的SafeLoadLibrary函數完成的找到庫後程序調用Windows API函數—GetProcAddress搜索DLL導出函數若找到匹配則返回所請求函數指針並將這個函數指針轉換成適當類型和調用使用完後調用FreeLibrary從內存中釋放庫
Kylix中使用Pascal RTL仿真函數實現SO庫動態裝入下面的例子只列出Kylix應用程序中與動態鏈接相關部分而非完整Kylix單元文件代碼
unit DynaForm;
interface
uses
SysUtilsClassesQcontrolsQforms
type
TForm = class(TForm)
Button: TButton;
procedure ButtonClick(Sender: TObject);
end;
var FormTForm
implementation
{$R *XFM}
type TComputeInteger = function (xIntegeryInteger)Integercdecl
//調用庫函數接口類型定義
procedure TFormButtonClick(SenderTObject)
var Handle Thandle
Compute TcomputeInteger
begin
Handle:=LoadLibrary(libMyFirstSOso)//動態裝入庫
if Handle<> then //找到庫
begin
Compute:=TcomputeInteger(GetProcAddress(HandleAdd)
//搜索庫函數Add並返回函數指針
if Assigned(Compute) then
ShowMessage(IntToStr(Compute())//使用庫函數
FreeLibrary(Handle)//釋放庫
end
else
ShowMessage(Library not found)
end
()用Linux自然代碼動態裝入
也可以使用Libc系統單元中的低級Linux函數這樣可使用更多參數更好地控制系統使用的Linux函數分別為dlopen(打開並裝入庫函數)dlsym(搜索庫函數)dlclose(釋放庫)因此上例中調用庫的代碼變為
procedure TFormButtonClick(SenderTObject)
var Handle Pointer
Compute TcomputeInteger
begin
Handle:=dlopen(libMyFirstSOso)//動態裝入庫
if Handle<>nil then //找到庫
begin
Compute:=TcomputeInteger(dlsym(HandleAdd)
//搜索庫函數Add並返回函數指針
if Assigned(Compute) then
ShowMessage(IntToStr(Compute())//使用庫函數
dlclose(Handle)//釋放庫
end
else
ShowMessage(Library not found)
end
()SO庫中窗體對象的使用
除了包含函數和過程的庫之外還可以將Kylix建立的窗體放在共享對象中這可以是對話框或其它窗體
生成新的庫對象之後只要在庫源文件的聲明部分增加對窗體單元文件的引用然後在窗體單元文件中編寫生成和使用窗體的導出函數下面的例子實現Kylix主程序通過調用SO庫窗體處理函數來激活模態對話框以選擇顏色並更新應用主窗體顏色步驟如下
◆ 創建具有特定功能的窗體單元文件ScrollF窗體對象為FormScroll下面代碼僅用於說明並非完整的程序
unit ScrollF;
interface
uses
SysUtils Classes QControls QForms;
type
TFormScroll = class(TForm) //對象及方法定義省略
end;
var FormScrollTformScroll
◆ 在窗體單元文件ScrollF的實現部分編寫使用窗體FormScroll的導出函數GetColor其功能是激活對話框對象FormScroll以選擇顏色並將顏色值返回代碼如下
function GetColor (Col: LongInt)LongIntcdecl
var
FormScrollTformScroll
begin
Result := Col; //函數返回缺省值
try
FormScroll := TFormScrollCreate (Application);
try
FormScrollSelectedColor := Col; //初始化顏色
if FormScrollShowModal = mrOK then //顯示對話框
Result := FormScrollSelectedColor; //返回顏色值
finally
FormScrollFree;
end;
except
on E: Exception do
MessageDlg (Error in FormDLL: +EMessage mtError [mbOK] );
end;
end;
◆ 在窗體文件ScrollF的定義部分增加導出函數GetColor的聲明代碼如下
function GetColor (ColLongInt)LongIntcdecl
◆ 在庫源文件FormSOdpr的定義部分增加對窗體單元ScrollF的引用代碼如下
library FormSO
uses
ScrollF in ScrollFpas {FormScroll}
exports
GetColor
end
◆ 編譯庫文件FormSOdpr生成SO庫執行文件libFormSOso
現在就可以在Kylix應用程序中以隱式或動態方法來調用庫libFormSOso中的窗體類函數GetColor
四應注意的問題
盡管SO庫為開發者帶來諸多好處但由於其與一般Kylix程序的差異如果把它當作後者一樣使用可能會帶來一些問題下面列出使用中可能出現的幾個問題
()在SO庫中大量引用CLX圖形類庫將對SO庫帶來不良影響
()如果編譯庫和主執行文件而不運行庫則會得到CLX代碼和數據的兩個拷貝例如可能得到兩個不同的全局Application對象庫中的Application對象不能正確初始化
()如果庫中需要大量調用CLX圖形類或CLX控件類對象建議最好使用軟件包(Package)—一種特殊的共享庫而若庫以非圖形類處理為主如數值計算則用SO庫更為方便
掌握並能熟練地使用共享對象庫技術將有助於開發出功能更強重用性更好擴展更為靈活的應用程序本文以實例方式通過SO與DLL的比較介紹了共享對象庫(SO)的功能及使用方法希望通過本文讀者能使用自己創建的SO庫來開發基於Linux的應用程序
From:http://tw.wingwit.com/Article/program/Delphi/201311/25048.html