本文用通俗易懂的语言介绍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}
{$SONAMElibsimpleso}
共享对象库的使用
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;
varFormTForm
implementation
{$R *XFM}
typeTComputeInteger = 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建立的窗体放在共享对象中这可以是对话框或其它窗体
生成新的库对象之后只要在库源文件的声明部分增加对窗体单元文件