静态不是很好吗?
直接就调用了。
动态还要dlopen dlclose一系列额外的动作。
---------------------------------------------------------------------------------------------------------
随便举个例子,我将不同语言的资源封装在dll中,有 中文.dll、english.dll、……
我软件运行时可以通过配置文件,或用户菜单更改需要载入的dll
而如果是静态调用的话,就得为每一种语言修改代码重新编译了
第二点,如果是动态调用,只要保证接口不变,dll内容是可以修改的
而如果是静态调用的话,修改了dll内容,就得将所有用到它的宿主程序重新编译
第三点,如果是静态调用的话,dll必须放在指定的几个搜索目录中
而如果是动态调用的话,可以在dlopen参数中指定一个具体的目录
------------------------------------------------------------------------------------
动态库的显式调用
库函数dlopen()将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如Apache Web服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。
dlopen()在dlfcn.h中定义,并在dl库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库名字。标志指明是否立刻计算库的依赖性。如果设置为RTLD_NOW的话,则立刻计算;如果设置的是RTLD_LAZY,则在需要的时候才计算。另外,可以指定RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。
当库被装入后,可以把 dlopen()返回的句柄作为给 dlsym()的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
下面详细说明一下这些函数。
-
dlerror
原型为:const char *dlerror(void);
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
-
dlopen
原型为:void *dlopen (const char *filename, int flag);
dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。
filename:如果名字不以/开头,则非绝对路径名,将按下列先后顺序查找该文件。
(1)用户环境变量中的LD_LIBRARY值;
(2)动态链接缓冲文件/etc/ld.so.cache
(3)目录/lib,/usr/lib
flag表示在什么时候解决未定义的符号(调用)。取值有两个:
1)RTLD_LAZY :表明在动态链接库的函数代码执行时解决。
2)RTLD_NOW :表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。
dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。
-
dlsym
取函数执行地址
原型为:void *dlsym(void *handle, char *symbol);
dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。
如程序代码:void (*add)(int x,int y); /*说明一下要调用的动态函数add*/
add=dlsym("xxx.so","add");/*打开xxx.so共享库,取add函数地址*/
add(89,369);/*带两个参数89和369调用add函数*/
-
dlclose:关闭动态链接库
原型为:int dlclose (void *handle);
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
隐式链接是指在代码中使用了了库中的代码,只是在链接时,链接器会把该库的符号信息以及导入函数的信息写入到生成的Exe文件的特定的区段中。当该程序加载时,操作系统会根据这个区段中的信息,来查找每个它需要的动态库,并根据这些动态库的导出表,与程序中的导入表相配对,以确定程序中使用的动态库中的代码在什么位置。这样应用程序就能够正确是链接到动态库中的代码了。如果在这个过程中,发生了错误,比如没有找到要求的库文件,或者库中没有你要使用的函数,程序的初始化就会失败,操作系统就会报错,并终止该程序的初始化,你的程序就死掉了。
显式链接一般是指在程序运行中,由程序代码用LoadLibrary和LoadLibraryEx函数来加载动态库。这两个函数仅仅是把库文件映射到你的进程地址空间中,并不会查找你要使用的函数的地址。如果这些函数成功,就会返回库文件在你的进程中的映像的基址,随便你就可以使用这个基址来调用GetProcAddress()函数来得到你要用的库中的函数的地址了。如果LoadLibrary函数失败,就会返回NULL,并不会强制杀死你的应用程序。
两种方法对于你的程序调用动态库时没有任何区别,只是你在编程时,步骤是不一样的。显式调用麻烦了点,但可以没有相应的lib库;隐式调用,使用起来比较简单,有函数的声明就可以了,但必须有lib库。
在VC中两种方式的具体方法:
一、动态库的隐示调用:
在 VC 工程中直接链接静态输入库XXX.lib,然后即可像调用其它源文件中
的函数一样调用DLL中的函数了。
二、动态库的显式调用:
显式调用动态库步骤:
1、创建一个函数指针,其指针数据类型要与调用的 DLL 引出函数相吻
合。
2、通过 Win32 API 函数LoadLibrary()显式的调用DLL,此函数返回
DLL 的实例句柄。
3、通过 Win32 API 函数GetProcAddress()获取要调用的DLL 的函数地
址,把结果赋给自定义函数的指针类型。
4、使用函数指针来调用 DLL 函数。
5、最后调用完成后,通过 Win32 API 函数FreeLibrary()释放DLL 函数。
1. 用 c 语言写动态库:
/*
* libsthc.h
* Declarations for function add
*/
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#ifdef __cplusplus
extern "C"
{
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
/*
* libsthc.c
* Implementation of function add declared in libsthc.h
* in c language
*/
#include "libsthc.h"
int add(int x, int y)
{
return x + y;
}
#makefile
libsthc.so:libsthc.o
gcc -shared libsthc.o -lc -o libsthc.so
libsthc.o:libsthc.c libsthc.h
gcc -fPIC -c libsthc.c -o libsthc.o
all:libsthc.so
clean:
rm -f *.o *.so
make 完成后,会生成一个动态库,即 libsthc.so 。为了使其他程序也可以使用该动态库,需要将库文件 libsthc.so 拷贝到 /usr/lib目录下 ( 由于权限的问题,一般要以 root 的身分进行拷贝 ) ,为了使其他程序也可以使用该动态库,需要将头文件 libsthc.h 拷贝到 /usr/include 目录下 ( 由于权限的问题 , 一般要以 root 的身分进行拷贝 ) 。
1.1 用 c 语言静态方式调用动态库 libsthc.so :
/*
* ctest.c
* Testing program for libsthc.so library
* in c languange
* by 玄机逸士
*/
#include "libsthc.h"
int main(void)
{
printf("%d/n", add(1, 2));
return 0;
}
#makefile:
ctest:ctest.o
gcc ctest.o -lsthc -o ctest
ctest.o:ctest.c
gcc -c ctest.c -o ctest.o
all:ctest
clean:
rm -f *.o ctest
1.2 用 c 语言动态方式调用动态库 libsthc.so :
/*cdltest.c*/
#include "stdio.h"
#include "stdlib.h"
#include "dlfcn.h"
int main(void)
{
void *handle;
int (*fcn)(int x, int y);
const char *errmsg;
/* open the library */
handle = dlopen("libsthc.so", RTLD_NOW);
if(handle == NULL)
{
fprintf(stderr, "Failed to load libsthc.so: %s/n", dlerror());
return 1;
}
dlerror();
//*(void **)(&fcn) = dlsym(handle, "add"); //ok
fcn = dlsym(handle, "add"); //ok
if((errmsg = dlerror()) != NULL)
{
printf("%s/n", errmsg);
return 1;
}
printf("%d/n", fcn(1, 5));
dlclose(handle);
return 0;
}
#makefile :
cdltest:cdltest.o
gcc cdltest.o -ldl -lsthc -o cdltest
cdltest.o:cdltest.c
gcc -c cdltest.c -o cdltest.o
all:cdltest
clean:
rm -f *.o cdltest
1.3 用 c++ 静态方式调用动态库 libsthc.so :
/*cpptest.cc*/
#include "libsthc.h"
using namespace std;
int main(void)
{
printf("%d/n", add(1, 2));
return 0;
}
#makefile:
cpptest:cpptest.o
g++ cpptest.o –o cpptest -lsthc
cpptest.o:cpptest.cc
g++ -c cpptest.cc -Wno-deprecated -o cpptest.o
all:cpptest
clean:
rm -f *.o cpptest
1.4 用 c++ 动态方式调用动态库 libsthc.so :
/*cppdltest.cpp*/
#include "stdio.h"
#include "stdlib.h"
#include "dlfcn.h"
int main(void)
{
void *handle;
int (*fcn)(int x, int y);
const char *errmsg;
/* open the library */
handle = dlopen("libsthc.so", RTLD_NOW);
if(handle == NULL)
{
fprintf(stderr, "Failed to load libsthc.so: %s/n", dlerror());
return 1;
}
dlerror();
*(void **)(&fcn) = dlsym(handle, "add"); //ok
//fcn = dlsym(handle, "add"); //not ok in c++
if((errmsg = dlerror()) != NULL)
{
printf("%s/n", errmsg);
return 1;
}
printf("%d/n", fcn(1, 5));
dlclose(handle);
return 0;
}
#makefile
cppdltest:cppdltest.o
g++ cppdltest.o -ldl -lsthc -o cppdltest
cppdltest.o:cppdltest.cpp
g++ -c cppdltest.cpp -o cppdltest.o
all:cppdltest
clean:
rm -f *.o cppdltest
2. 用 c++ 语言写动态库:
/*
* libsthcpp.h
* Declarations for function cppadd
*/
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#ifdef __cplusplus
extern "C"
{
#endif
int cppadd(int x, int y);
#ifdef __cplusplus
}
#endif
/*
* libsthcpp.cpp
* Implementation of function cppadd declared in libsthcpp.h
* in c++ language
*/
#include "libsthcpp.h"
int cppadd(int x, int y)
{
return x + y;
}
#makefile
libsthcpp.so:libsthcpp.o
g++ -g -shared -Wl libsthcpp.o -lc -o libsthcpp.so
libsthcpp.o:libsthcpp.cc libsthcpp.h
g++ -g -fPIC -c libsthcpp.cc -o libsthcpp.o
all:libsthcpp.so
clean:
rm -f *.o *.so
make 完成后,会生成一个动态库,即 libsthcpp.so 。为了使其他程序也可以使用该动态库,需要将库文件 libsthcpp.so 拷贝到/usr/lib 目录下 ( 由于权限的问题,一般要以 root 的身分进行拷贝 ) ,为了使其他程序也可以使用该动态库,需要将头文件libsthcpp.h 拷贝到 /usr/include 目录下 ( 由于权限的问题 , 一般要以 root 的身分进行拷贝 ) 。
2.1 用 c 语言静态方式调用动态库 libsthcpp.so :
/*
* ctest.c
* Testing program for libsthcpp.so library
* in c languange
* by 玄机逸士
*/
#include "libsthcpp.h"
int main(void)
{
printf("%d/n", cppadd(1, 2));
return 0;
}
#makefile
ctest:ctest.o
gcc ctest.o -lsthcpp -o ctest
ctest.o:ctest.c
gcc -c ctest.c -o ctest.o
all:ctest
clean:
rm -f *.o ctest
2.2 用 c 语言动态方式调用动态库 libsthcpp.so :
/*cdltest.c*/
#include "stdio.h"
#include "stdlib.h"
#include "dlfcn.h"
int main(void)
{
void *handle;
int (*fcn)(int x, int y);
const char *errmsg;
/* open the library */
handle = dlopen("libsthcpp.so", RTLD_NOW);
if(handle == NULL)
{
fprintf(stderr, "Failed to load libsthc.so: %s/n", dlerror());
return 1;
}
dlerror();
//*(void **)(&fcn) = dlsym(handle, "cppadd"); //ok in c and c++
fcn = dlsym(handle, "cppadd"); //ok in c, but not in c++
if((errmsg = dlerror()) != NULL)
{
printf("%s/n", errmsg);
return 1;
}
printf("%d/n", fcn(1, 5));
dlclose(handle);
return 0;
}
#makefile
cdltest:cdltest.o
gcc cdltest.o -ldl -lsthcpp -o cdltest
cdltest.o:cdltest.c
gcc -c cdltest.c -o cdltest.o
all:cdltest
clean:
rm -f *.o cdltest
2.3 用 c++ 语言静态方式调用动态库 libsthcpp.so :
/*
* cpptest.cpp
* Testing program for libsthc.so library written in c language
* in c++ languange
* by 玄机逸士
*/
#include "libsthcpp.h"
#include "iostream.h"
int main(void)
{
cout << cppadd(1, 2) << endl;
return 0;
}
#makefile
cpptest:cpptest.o
g++ cpptest.o -lsthcpp -o cpptest
cpptest.o:cpptest.cpp
g++ -c cpptest.cpp -Wno-deprecated -o cpptest.o
all:cpptest
clean:
rm -f *.o cpptest
2.4 用 c++ 语言动态方式调用动态库 libsthcpp.so :
/*cppdltest.cpp*/
#include "stdio.h"
#include "stdlib.h"
#include "dlfcn.h"
int main(void)
{
void *handle;
int (*fcn)(int x, int y);
const char *errmsg;
/* open the library */
handle = dlopen("libsthcpp.so", RTLD_NOW);
if(handle == NULL)
{
fprintf(stderr, "Failed to load libsthc.so: %s/n", dlerror());
return 1;
}
dlerror();
*(void **)(&fcn) = dlsym(handle, "cppadd"); //ok in c and c++
//fcn = dlsym(handle, "cppadd"); //ok in c, but not in c++
if((errmsg = dlerror()) != NULL)
{
printf("%s/n", errmsg);
return 1;
}
printf("%d/n", fcn(1, 5));
dlclose(handle);
return 0;
}
#makefile
cppdltest:cppdltest.o
g++ cppdltest.o -ldl -lsthcpp -o cppdltest
cppdltest.o:cppdltest.cpp
g++ -c cppdltest.cpp -o cppdltest.o
all:cppdltest
clean:
rm -f *.o cppdltest