Days in DLL(一)

来源:互联网 发布:java大数据是什么 编辑:程序博客网 时间:2024/05/17 07:35

1,  静态库与动态库的区别:

      最显著的区别是:  静态库 生成EXE文件后可以遗弃,  动态库却要一直跟随。


      静态库的原理是: 在编译链接可执行文件(exe)时, 链接器将库中的函数与数据 与应用程序的其他模块组合起来, (这里我看作是inline 或是 insert)

      动态库的原理是: 首先动态库也有类似静态库的文件(lib)(确实让人费解, 这里我们称之引入库文件, 不知大家记得函数申明吗? 所谓的申明在h文件, 实现在cpp文件?这里就是答案了。 这里的引入库文件充当的就是h文件的角色。在编译的时候跟静态库的原理相似, lib文件组合入应用程序中, 而dll仍在外部。这也是为什么生成EXE文件后动态库一直跟随的原因)


    其实在上面已经道出动态链接库的优势所在。。。。(plug and play?)当我们想要更改程序里面的某一个函数的实现, easy, 只需改变其dll。还有, 这里就意味着分工合作实现的可能性,  每个人只需为自己的dll文件负责。。。更多详细内容,请百度, 这里就不多累赘了。


2, 简单制作dll && 使用之:

      制作dll,  

            本人用的是VS2013, 新建 控制台项目 下一步 选择dll  空项目, 下面是其代码:

MyDll.cppint sharenum = 0;__declspec(dllexport) int add(int a, int b){return a + b;}__declspec(dllexport) double add(double a, double b){return a + b;}__declspec(dllexport) int minus(int a, int b){return a - b;}__declspec(dllexport) int shareNum(){return sharenum;}__declspec(dllexport) int sharenumplus(int num){sharenum += num;return shareNum();}


    使用dll: 

      正如我上面(第一节所言, 编译需要lib 以及dll文件, 在本工程Debug中, 复制其lib  dll。)下面是使用的代码:

testDll.cpp#include "stdio.h"#pragma comment (lib, "ConsoleApplication1.lib")_declspec(dllimport) int add(int a, int b);extern int minus(int a, int b);extern int shareNum();extern int sharenumplus(int num);int main(){printf("1 + 2 = %d\n", add(1, 2));printf("1 - 2 = %d\n", minus(1, 2));printf("shareNum is %d\n", shareNum());char ch;scanf("%c", &ch);printf("shareNum + 1 is %d\n", sharenumplus(1));scanf("%c", &ch);return 0;}


温馨提示:

注1: ConsoleApplication1.lib 因为我上一个工程的名称为之,这里值得注意的是 _declspec(dllimport) 与 extern (顺道说一下上文的_declspec(dllexport), 它表示该方法可以被其他程序所调用,maybe private ->>>   public?), 回到正题, 这俩者的效果是一致的, 也就是声明外部调用的方法, 不同的是:_declspec(dllimport)更高效。

注2:更为常见的是:头文件定义好所有函数, 源文件实现, 这里用一个技巧:

//h文件:#ifndef DLL1_API#define DLL1_API _declspec(dllimport)#endif // !DLL1_APIDLL1_API int add(int a, int b);。。。。。。//cpp:#define DLL1_API _declspec(dllexport)#include "dllHeader.h"int add(int a, int b){return a + b;}。。。

这里有个好处, 即:可以有一个类似doc的文件(h文件)可以参考,方便使用者。 当然最显著的特点是, 使用dll的时候, 只需加载lib, 函数申明可以用#include头文件解决。

#include "dllHeader.h"#include "stdio.h"#pragma comment (lib, "MyDll.lib")int main(){printf("1 + 2 = %d\n", add(1,2));char ch;scanf("%c", &ch);}

 

3, 从DLL中导出类:

   基本格式:

  

//**.hclass DLL1_API robot{public :void sayhello();};//**.cppvoid robot::sayhello(){printf("hello world");}//关于DLL1_API, 请看上文温馨2

不知读者是否知道 transient,  这个是在序列化里面的(Java), 所修饰的数据将不参与序列化过程。

而这里的_declspec(export) 是否有异曲同工之妙呢? 只有用这个修饰的才能从dll 中导出。



4. 解决名字改编问题:

问题描述:

编译器在生成dll文件的时候, 会对其名字进行改编,   不同编译器, 改编的形式或许有所不同, 这就有可能发生A编译生成的dll, B不能使用的情况。

(2015/04/22 其原因是:C 中没有重载的概念,而C++有,也就是说,C++编译生成名字的时候,会加上其参数列表的内容,测试代码如下:)

//override.c#include "stdio.h"int hello(int a)  {        printf("arg is %d\n", a);         return 0;}int hello(){        printf("no arg");        return 0;}int main() {                return 0;}

结果:

插播一下(其实命名很重要,一个好的文件,函数,字段名字是无需额外的注解的,所以学好英语很重要  ovo    请忽略我,我一般百度翻译。。)

解决方法:

方法一:
添加限定符:      extern “C”    // C必须是大写的。表示用C编译器编译 
函数调用约定:   _stdcall   

即为:
//h文件:#ifndef DLL1_API#define DLL1_API <strong>extern "C" </strong>_declspec(dllimport)#endif // !DLL1_APIDLL1_API int  <strong>_stdcall</strong>  add(int a, int b);。。。。。。//cpp:#define DLL1_API<strong> <span style="font-family: Arial, Helvetica, sans-serif;">extern "C"</span></strong><span style="font-family: Arial, Helvetica, sans-serif;"><strong> </strong>_declspec(dllexport)</span>#include "dllHeader.h"int  <strong>_stdcall</strong>  add(int a, int b){return a + b;}。。。

缺点:  不支持class, 不允许重载函数的第二个 C 链接(我理解为 不允许函数重载, 即函数不能同名)


方法二: 使用模块定义文件DEF


1, 在dll工程中添加def文件
2, 编写def文件

LIBRARY MyDllEXPORTSadd                          //sample1myminus= minus     //sample2
sample1:     表示源文件中定义的add 将以 add 符号名导出。
sample2:     表示源文件中定义的minus将以myminus符号名导出。(使用时, 需要改动h文件即可)

使用dumpbin一查,   发现如下情况:

<strong>1    0 00011109 add = @ILT+260(_add@8)2    1 00011005 minus = @ILT+0(_minus)3    2 00011005 myminus = @ILT+0(_minus)</strong>

恩, 看样子,   这是别名。。。。。。。。。不过经检验, 确实可以用myminus调用其函数

而且   看样子, 这个不能解决函数重载的问题


结语:

这里是我的一人之言, 总有疏漏, 欢迎打脸。。。微笑

0 0