dll--二进制层面的复用

积木式思想其实是很自然的一个过程,从c的库函数到C++的标准库,再到dll、com、com+都是这种思想推动下的结果,和现实生活中的人们的思维方式并无二致,只不过软件是在一个虚拟的世界中,并分化出许多不同的形式。

二进制层面的复用主要是依赖于现代loader提供的强大能力,它使得二进制的功能块(dll,exe,sys,ocx...)可以在内存中组合成一个相互合作的系统,也就是我们常说的动态链接(相对于静态链接在编译时期形成一个单一的文件)。

剩下的问题就是怎么合作的问题了,导入导出函数就是功能块之间互联互通的一个窗口,包括com不管理论多么复杂高深多么封装,也离不开导出函数,二进制复用看来是软件工程中一种终极的复用方式了。

c++中允许函数重载,对此c++实际上并不去根据参数决定调用哪个函数,而是直接给重载函数取不同的名称(mangling mechanism),导出的时候也是导出这些被mangled的名字 ,除非该函数声明为c方式处理,则函数名称不会被二次加工。

编译dll时会附带一个lib文件,包含了dll的导出函数表,导入程序中可以包含该lib文件以及相应的头文件,使用时和使用未编译的源码一样,也可以使用loadlibrary 和getprocaddress在运行时使用dll提供的功能,这里getprocaddress时需要注意的是,如果dll只导出了函数的ordinal,那么需要用makentresource(ordinal)做一下转换,getprocaddress会根据这个ordinal在导出表中找到对应的函数地址。

为了保持导入程序中使用的函数名称和dll导出的函数名称一致(这样可以保证根据name在导出表中找到对应的函数地址),如果dll中使用了extern "C"方式处理函数,那么导入函数中也要使用extern "C"方式处理相应函数声明,否则名称不会一致,如果使用了extern "C" 后函数前加上__stdcall声明,那么name mangling mechanism 又被激活,实际上得出的函数名称还是C++风格的名称。还有一种保持一致的方式是在生成dll时用def文件对导出的函数名称进行说明,例如:

LIBRARY DLL1

EXPORTS

add//函数名称不被转换

subtract

inc=increase//原始名称专成目标名称

sub=subb

abc  @1//对应的ordinal

cde  @2

ps:

1.dll中有数据段和代码段,代码段在内存中只有一份,为多个进程共享,数据段默认都是进程私有的,这个操作系统在做内存页映射的时候会处理,数据段也可以被声明为共享的,用于进程间通信,但这样可能导致恶意进程非法改动共享数据,破坏其他进程。声明方法(http://blog.csdn.net/xuplus/article/details/2291860

2.lib文件有两种1.用于静态连接,包括声明和实现2.用于动态连接,只包括声明,相当于一个头文件,lib文件的格式和.a(archive)文件格式类似,都是一些coff obj文件的集合。

3.extern等同于__declspec(dllimport)

4.dll中类的导出实际上是类的成员函数的导出,其中包括构造函数(or 系统默认构造函数),当然也可以部分导出类成员函数(如果你的导入程序中没有用到其他成员函数的话),类的数据成员都是在导入程序的空间中。

相信世界是平的
谨记四个字“修身养性”
大江东去浪淘尽英雄,再牛B的人物最后也是一掊土
向善不是目的,而是抚慰心灵,更多的感受幸福,感谢别人给你行善的机会
相信老子的话:万物生于有,有生于无,一切的道理都源于一个无法证明的假设
我是好是坏就自然而然的摆在那里,并不会因为别人的评价而改变什么,我也不需要别人用一张纸来说明我什么,世间最难得的是自由



支持大额赞助:
原文地址:https://www.cnblogs.com/sky-view/p/3343219.html