windows中dll和linux中so的动态链接库的详解
2011-07-17 18:32
806 查看
1 生成windows中静态链接的静态库
和2中linux的完全相同。
2 生成和使用linux中的.a静态链接库。
如下例:
linux中.a文件的名字是有规则的lib[name].a
于是得到了libhellos.a这么一个静态链接库
不知道g++为什么不行。
3 生成windows中的dll,并进行隐式链接使用。
在上面hellos.h hellos.c hellos 三个文件的基础上。
生成dll:gcc -shared -o helloc.dll helloc.c
使用dll:gcc -o hello main.c -L. -lhellos
-----------------------------------------------------------------------------------------------------------------------------------------------------------
这样就将hellos.dll和hello链接上了。删除hellos.dll时候会报错找不到dll。
感觉这算是gcc提供的一种机制了?
由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码,此处属于其中的一种方式把。
注意:这里并没有进行windows中下面的各种复杂声明。
windows中的两种链接方法的原理性解释:
链接库分为静态链接库和动态链接库,而动态链接库在使用时,又进一步分为装载时链接和运行时链接。装载时链接是指该动态链接库是在程序装入时进行加载链接的,而运行时链接是指该动态链接库是在程序运行时执行LoadLibrary(或LoadLibraryEx,下同)函数动态加载的。因此,由于动态链接库有这两种链接方式,所以在编写使用DLL的程序时,就有了两种可选方案。
可能有人会问“为什么需要装载时链接?直接静态链接不就行了吗?”,这是模块化程序设计的需要。试想,如果你开发一个很大的程序,并且经常需要更新。如果你选择静态链接,那么每次更新就必须更新整个exe文件,而如果你把需要经常更新的模块做成dll,那么只需要更新这个文件即可,每次程序运行时加载这个更新的文件即可。
另两个重要的、需要区分的概念是:对象库(Object Library)和导入库(Import Library)。对象库是指普通的库文件,比如C运行时库libc.lib;而导入库是一种比较特殊的对象库文件,与一个动态链接库相对应。它们都有后缀.lib,并且都仅在程序编译链接时使用,被链接器用来解析函数调用。然而,导入库不包含代码,它只为链接器提供动态链接库的信息,以便于链接器对动态链接库中的对象作恰当地链接。
关于__stdcall:如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用约定声明为__stdcall方式,WINAPI、CALLBACK都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionName@number ,而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionName。
这些调用约定是给编译器使用的,用来确定生成最终生成的符号的。
这个对象库(Object Library至今不知道怎么生成)囧了,暂时先不管他。
更细可参考:http://blog.csdn.net/ljx0305/article/details/4513074
---------------------------------------------------------------------------------------------------------------------------------------------------------
4 生成windows中的dll,并进行显示加载
先写一个最简单的显示动态加载:
dlltest.h:
dlltest.cpp
usedll.cpp
编译选项:
g++ -shared -o dlltest.dll dlltest.cpp //生成动态dll。使用了__declspec(dllexport)关键字将函数导出。
g++ -o usedll usedll.cpp
运行即有结果
This in in NumberList!
This is in LetterList!
下面解释一下:extern “C” 和__stdcall ,__cdecl存在的原因。
说的简单点就是为了让名字清晰,可以被使用dll的人正确找到函数。可以使用VC带的Dependency工具查看dll中的export的函数名字。
1 默认c语言在编译时候,使用__cdecl方式进行编译,这个convention包括很多东西,寄存器,堆栈,命名等。这里编译时候c不改变函数的名字。
所以在动态寻找函数名字时候能够正确的找到。
2 Win32中都使用__stdcall,这样的话命名方式与__cdecl有些不同,如果不使用def文件,而使用__declspec(dllexport)导出的话,上面dlltest.cpp中的2个函数
会变成
3 extern “C” 这是为了避免c++对c中的命名的更改。
因为c++中支持
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为: void foo( int x, int y ); 该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y)编译生成的符号是不相同的,后者为_foo_int_float。
所以名字也会发生变化,这样我们也会找不到对应的函数,于是加上extern “C”告诉c++编译器按照c语言的方式去编译!
然后其实使用的编译器g++/gcc,是否定义def文件,dll中的调用方式,caller中的调用方式都会影响到调用是否成功。
水木上总结的是:
测试环境 vc6.0绿色版
新建工程 testdll(win32 dynamic lib)用来生成dll
新建工程 testmydll(win32 console app),显示调用dll
生成dll文件testdll.cpp
测试dll文件testmydll.cpp
采用depends观察dll的输出函数名
1 使用_declspec(dllexport)关键字,C编译,_cdecl
导出函数名 fnTestdll _cdecl调用成功, __stdcall调用失败
2 使用_declspec(dllexport)关键字,C编译,_stdcall
导出函数名 _fnTestdll@4 _cdecl调用失败, __stdcall调用失败
3 使用_declspec(dllexport)关键字,C++编译,_cdecl
导出函数名 ?fnTestdll@@YAHH@Z _cdecl调用失败, __stdcall调用失败
4.使用_declspec(dllexport)关键字,C++编译,_stdcall
导出函数名 ?fnTestdll@@YGHH@Z _cdecl调用失败, __stdcall调用失败
5.使用DEF文件 , C编译, _cdecl
导出函数名 fnTestdll, _cdecl调用成功, __stdcall调用失败
6.使用DEF文件, C编译, _stdcall
导出函数名 fnTestdll, _cdecl调用失败, __stdcall调用成功
7.使用DEF文件 , C++编译, _cdecl
导出函数名 fnTestdll, _cdecl调用成功, __stdcall调用失败
8.使用DEF文件, C++编译, _stdcall
导出函数名 fnTestdll, _cdecl调用失败, __stdcall调用成功
结论: 尽可能还是用DEF文件定义输出函数吧:)
特别是需要给非C++程序调用(使用__stdcall)的时候.;
5 linux中隐式和显式调用SO共享文件(未完待续...累了,睡觉去)
和2中linux的完全相同。
2 生成和使用linux中的.a静态链接库。
如下例:
/* hellos.h */ #ifndef _HELLO_S_H #define _HELLO_S_H #include <stdio.h> void printS(char* str); #endif输入命令:
gcc -c -o hellos.o hellos.c ar cqs libhellos.a hellos.o
linux中.a文件的名字是有规则的lib[name].a
于是得到了libhellos.a这么一个静态链接库
2:主程序 /* main.c */ #include "hellos.h" void main() { char* text = "Hello World!\n"; printS(text); } 编译链接: gcc -o hello main.c -L. -lhellos 注意-main.c要放在-L. -lhellos前面,否则出错。 然后运行hello可以看到输出 print in static way: Hello World!删除libhellos.a和hellos.*后, 程序仍然正常运行。
不知道g++为什么不行。
3 生成windows中的dll,并进行隐式链接使用。
在上面hellos.h hellos.c hellos 三个文件的基础上。
生成dll:gcc -shared -o helloc.dll helloc.c
使用dll:gcc -o hello main.c -L. -lhellos
-----------------------------------------------------------------------------------------------------------------------------------------------------------
这样就将hellos.dll和hello链接上了。删除hellos.dll时候会报错找不到dll。
感觉这算是gcc提供的一种机制了?
由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码,此处属于其中的一种方式把。
注意:这里并没有进行windows中下面的各种复杂声明。
windows中的两种链接方法的原理性解释:
链接库分为静态链接库和动态链接库,而动态链接库在使用时,又进一步分为装载时链接和运行时链接。装载时链接是指该动态链接库是在程序装入时进行加载链接的,而运行时链接是指该动态链接库是在程序运行时执行LoadLibrary(或LoadLibraryEx,下同)函数动态加载的。因此,由于动态链接库有这两种链接方式,所以在编写使用DLL的程序时,就有了两种可选方案。
可能有人会问“为什么需要装载时链接?直接静态链接不就行了吗?”,这是模块化程序设计的需要。试想,如果你开发一个很大的程序,并且经常需要更新。如果你选择静态链接,那么每次更新就必须更新整个exe文件,而如果你把需要经常更新的模块做成dll,那么只需要更新这个文件即可,每次程序运行时加载这个更新的文件即可。
另两个重要的、需要区分的概念是:对象库(Object Library)和导入库(Import Library)。对象库是指普通的库文件,比如C运行时库libc.lib;而导入库是一种比较特殊的对象库文件,与一个动态链接库相对应。它们都有后缀.lib,并且都仅在程序编译链接时使用,被链接器用来解析函数调用。然而,导入库不包含代码,它只为链接器提供动态链接库的信息,以便于链接器对动态链接库中的对象作恰当地链接。
关于__stdcall:如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用约定声明为__stdcall方式,WINAPI、CALLBACK都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionName@number ,而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionName。
这些调用约定是给编译器使用的,用来确定生成最终生成的符号的。
这个对象库(Object Library至今不知道怎么生成)囧了,暂时先不管他。
更细可参考:http://blog.csdn.net/ljx0305/article/details/4513074
---------------------------------------------------------------------------------------------------------------------------------------------------------
4 生成windows中的dll,并进行显示加载
先写一个最简单的显示动态加载:
dlltest.h:
#ifndef _DLLTEST_H_ #define _DLLTEST_H_ #include <iostream.h> #include <stdio.h> #include <windows.h> extern "C" __declspec(dllexport) void NumberList(); extern "C" __declspec(dllexport) void LetterList(); #endif
dlltest.cpp
#include "dlltest.h" extern "C" __declspec(dllexport) void NumberList() { cout << "This in in NumberList" << endl; } extern "C" __declspec(dllexport) void LetterList() { cout << "This is in LetterList!" << endl; }
usedll.cpp
#include <windows.h> #include <iostream.h> #include <stdio.h> #include <conio.h> typedef void (*cfunc)(); cfunc NumberList; cfunc LetterList; int main() { HINSTANCE hLib=LoadLibrary("DLLTEST.DLL"); if(hLib==NULL) { cout << "Unable to load library!" << endl; return 0; } NumberList=(cfunc)GetProcAddress((HMODULE)hLib, "NumberList"); LetterList=(cfunc)GetProcAddress((HMODULE)hLib, "LetterList"); if((NumberList==NULL) || (LetterList==NULL)) { cout << "Unable to load function(s)." << endl; FreeLibrary((HMODULE)hLib); return 0; } NumberList(); LetterList(); FreeLibrary((HMODULE)hLib); return 0; }
编译选项:
g++ -shared -o dlltest.dll dlltest.cpp //生成动态dll。使用了__declspec(dllexport)关键字将函数导出。
g++ -o usedll usedll.cpp
运行即有结果
This in in NumberList!
This is in LetterList!
下面解释一下:extern “C” 和__stdcall ,__cdecl存在的原因。
说的简单点就是为了让名字清晰,可以被使用dll的人正确找到函数。可以使用VC带的Dependency工具查看dll中的export的函数名字。
1 默认c语言在编译时候,使用__cdecl方式进行编译,这个convention包括很多东西,寄存器,堆栈,命名等。这里编译时候c不改变函数的名字。
所以在动态寻找函数名字时候能够正确的找到。
2 Win32中都使用__stdcall,这样的话命名方式与__cdecl有些不同,如果不使用def文件,而使用__declspec(dllexport)导出的话,上面dlltest.cpp中的2个函数
会变成
NumberList@0 [code]LetterList@0这样如果在usedll的代码中仍然使用NumberList去找的话,就会找不到。
3 extern “C” 这是为了避免c++对c中的命名的更改。
因为c++中支持
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为: void foo( int x, int y ); 该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangledname”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y)编译生成的符号是不相同的,后者为_foo_int_float。
所以名字也会发生变化,这样我们也会找不到对应的函数,于是加上extern “C”告诉c++编译器按照c语言的方式去编译!
然后其实使用的编译器g++/gcc,是否定义def文件,dll中的调用方式,caller中的调用方式都会影响到调用是否成功。
水木上总结的是:
测试环境 vc6.0绿色版
新建工程 testdll(win32 dynamic lib)用来生成dll
新建工程 testmydll(win32 console app),显示调用dll
生成dll文件testdll.cpp
测试dll文件testmydll.cpp
采用depends观察dll的输出函数名
1 使用_declspec(dllexport)关键字,C编译,_cdecl
导出函数名 fnTestdll _cdecl调用成功, __stdcall调用失败
2 使用_declspec(dllexport)关键字,C编译,_stdcall
导出函数名 _fnTestdll@4 _cdecl调用失败, __stdcall调用失败
3 使用_declspec(dllexport)关键字,C++编译,_cdecl
导出函数名 ?fnTestdll@@YAHH@Z _cdecl调用失败, __stdcall调用失败
4.使用_declspec(dllexport)关键字,C++编译,_stdcall
导出函数名 ?fnTestdll@@YGHH@Z _cdecl调用失败, __stdcall调用失败
5.使用DEF文件 , C编译, _cdecl
导出函数名 fnTestdll, _cdecl调用成功, __stdcall调用失败
6.使用DEF文件, C编译, _stdcall
导出函数名 fnTestdll, _cdecl调用失败, __stdcall调用成功
7.使用DEF文件 , C++编译, _cdecl
导出函数名 fnTestdll, _cdecl调用成功, __stdcall调用失败
8.使用DEF文件, C++编译, _stdcall
导出函数名 fnTestdll, _cdecl调用失败, __stdcall调用成功
结论: 尽可能还是用DEF文件定义输出函数吧:)
特别是需要给非C++程序调用(使用__stdcall)的时候.;
5 linux中隐式和显式调用SO共享文件(未完待续...累了,睡觉去)
相关文章推荐
- Java框架JNA调用C方法(windows链接库dll文件、linux链接库so文件)
- python 调用so dll动态链接库
- windows系统中出现dll动态链接库错误该怎么办?
- linux&Windows动态链接库技术实现和设计程序常用的技术
- linux下动态链接问题(so文件的编写与调用)
- windows下查看.dll文件和Linux查看.so文件中函数的实际名称
- 动态链接 - dll和so文件区别与构成
- Linux中关于JAVA调用C生成的.so动态链接库
- so-a--linux---静态--动态--链接库
- Linux下的动态链接库.so文件的使用
- Linux下编译出来的动态链接库缺少so后缀的问题解决
- linux下的.a和.so,对比windows下的lib和dll
- Linux-(C/C++)动态链接库生成以及使用(libxxx.so)
- Linux动态链接之五:运行时显式加载共享文件.so
- Linux下的动态链接库.so文件的使用
- linux下C++动态链接C++库示例详解
- Linux-(C/C++)动态链接库生成以及使用(libxxx.so)
- Loadlibrary:将Windows的动态链接库移植到Linux下
- windows和linux动态链接库比较
- [NOTE] Windows&Linux动态链接库学习笔记