本文用通俗易懂的語言介紹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 (A
Char
B
Char)
Integer
cdecl
overload
begin
Result := Ord (A) + Ord (B)
end
function Add (A
Integer
B
Integer)
Integer
cdecl
overload
begin
Result := A + B
end
function Double (N
Integer)
Integer
cdecl
begin
Result := N *
end;
exports
Add (A
Integer
B
Integer)
Add (A
Char
B
Char) name
AddChar
Double
SO庫文件中的函數重載
SO庫也可以使用重載函數(即多個函數使用相同名稱
不同參數)
使用時需在重載的函數聲明後標上overload指令
Kylix可以用原名稱導出一個重載函數
在exports從句中表示其參數表
若要導出多個重載函數
則要在exports從句中用name字句指定不同名稱
以區別重載
這可從上面的例子MyFirstSO中看出
Add是重載函數
為調用時區分
一個用原函數聲明Add導出
另一個用AddChar導出
SO庫的特殊編譯指令
編譯後生成的SO庫運行文件使用lib前綴和
so擴展名
考慮到實際命名規則與版本和支持符號鏈
Kylix在Object Pascal語言中引入了幾個特殊編譯指令
這些在Delphi中沒有什麼意義
庫源文件MyFirstSO
dpr編譯後產生的執行文件為libMyFirstSO
so
◆ $SOPREFIX 改變名稱前綴
默認為lib(正常庫)或bpl(Kylix包)
用前綴區別兩種庫是因為Linux的庫用單一擴展(
so)
◆ $SOSUFFIX 在庫名與擴展名之間增加文本
指定版本或其它信息
◆ $SOVERSION 在擴展名之後增加版本號
◆ $SONAME 表示相關符號鏈名
由編譯器自動生成
例如
下列代碼生成庫libsimple
so
和符號鏈libsimple
so
library simple
uses
SysUtils
Classes
//函數定義省略
{$SOVERSION
}
{$SONAME
libsimple
so
}
共享對象庫的使用
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定義從句
則庫函數可以和一般局部函數一樣使用
比如
要使用libMyFirstSO
so中的Add函數
則只要在應用程序中增加下面語句
function Add (A
Integer
B
Integer)
Integer
cdecl
external
libMyFirstSO
so
顯式鏈接
顯式鏈接是應用程序在執行過程中可根據實際需要隨時加載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
SysUtils
Classes
Qcontrols
Qforms
type
TForm
= class(TForm)
Button
: TButton;
procedure Button
Click(Sender: TObject);
end;
var Form
TForm
implementation
{$R *
XFM}
type TComputeInteger = function (x
Integer
y
Integer)
Integer
cdecl
//調用庫函數接口類型定義
procedure TForm
Button
Click(Sender
TObject)
var Handle
Thandle
Compute
TcomputeInteger
begin
Handle:=LoadLibrary(
libMyFirstSO
so
)
//動態裝入庫
if Handle<>
then //找到庫
begin
Compute:=TcomputeInteger(GetProcAddress(Handle
Add
)
//搜索庫函數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 TForm
Button
Click(Sender
TObject)
var Handle
Pointer
Compute
TcomputeInteger
begin
Handle:=dlopen(
libMyFirstSO
so
)
//動態裝入庫
if Handle<>nil then //找到庫
begin
Compute:=TcomputeInteger(dlsym(Handle
Add
)
//搜索庫函數Add
並返回函數指針
if Assigned(Compute) then
ShowMessage(IntToStr(Compute(
))
//使用庫函數
dlclose(Handle)
//釋放庫
end
else
ShowMessage(
Library not found
)
end
(
)SO庫中窗體對象的使用
除了包含函數和過程的庫之外
還可以將Kylix建立的窗體放在共享對象中
這可以是對話框或其它窗體
生成新的庫對象之後
只要在庫源文件的聲明部分增加對窗體單元文件
From:http://tw.wingwit.com/Article/program/Delphi/201311/24683.html