博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动态链接库编程 ( DLL ) 浅见
阅读量:4054 次
发布时间:2019-05-25

本文共 3012 字,大约阅读时间需要 10 分钟。

首先我们明白“DLL”编程是很重要的:在windows API 中有3个重要的DLL

 

Kernel32.dll:它包含用于管理内存、进程和线程的各个函数;

User32.dll: 它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;

GDI32.dll:它包含用于画图和显示文本的各个函数。

 

对于“动态链接库”和“静态链接库”的区别:简单来说,前者在编译的时候链接的是生成的 DLL.lib “引导库”文件,不是.dll 本身。DLL.lib “引导库”文件中包含了 .dll中的变量信息,函数声明信息,还有其他的一些信息、、、在程序运行的时候还需要通过一个路径去找.dll库的所在(一般的寻找顺序是:Debug ---> 本目录 --- >system32 --- > system --- > 其他系统目录--- > Dll.lib提供的目录),然后完成程序的正常运行。同时在DLL加载进客户端程序的时候,又分为两种行为:第一种是将库直接写入 Link中,那么程序运行时就加载了此库,但是很多情况下是用不了那么的函数的,所以很占内存,所以第二种就是动态加载DLL。 而对于“静态链接库”来说,在编译的时候,就将库内容链接到exe中,所以在客户端程序运行的时候不需要再提供库了。

 

下面看看DLL编程:

 

我们任意生成一个DLL程序,链接后会生成3个重要文件:

1、.dll:主要文件,包含最多的信息,函数的实现、、、

2、.exp:包含的是输出信息、、、

3、.lib:包含的是输入信息、、、

 

对于函数能够被客户端程序调用,我们需要注意很多问题:

 

1、  为了是函数能够被客户端调用,也可以被自身调用,我们需要做一些手脚,呵呵、、、

 

在 dll.h 中:

#ifdefDLL1_API                         // if 已经定义咯  DLL1_API

#else                     

#define DLL1_API _declspec( dllimport ) //注意:dllimport是外部函数被使用的必须条件

 

#endif

 

DLL1_API int add( int a, int b);           // 声明是外部变量( 函数 )

DLL1_API int subtract( int a, int b );        //声明是外部变量( 函数 )

 

在dll.cpp中:

#define DLL1_API _declspec( dllexport )

int add( int a, int b)      

                                                                                    

      return ( b + a );

}

 

int subtract( int a, int b )

{

      return ( a - b );

}

我们可以解释一下:if 是内部程序调用的话,我们知道,由于在cpp中已经定义了

#define DLL1_API _declspec( dllexport )

那么就不需要在定义DLL1_API了。但是对于外部程序而言,只要客户端没有定义,那么我们需要定义#define DLL1_API_declspec( dllimport ),对于客户端程序调用的条件!

 

注:对于_declspec( dllimport )和_declspec( dllexport )在上篇已经说过、、、

 

2、 我们知道,对于C++而言,在连接DLL的时候,我们的函数名已经不一定是原生态的名字咯,呵呵,为什么这么说呢?因为C++编译器有一个“名字改编”机制,是有影响的!不知道的可以看一下:在 Dos 下通过 dumpbin可以看到:

dumpbin –exports Dll1.dll 可以看输出( 导出 )信息

dumpbin –imports Dll1.dll 可以看输入( 导入 )信息

 

 

我们可以看到:原本我的程序代码中的函数是:

DLL1_API int add( int a, int b);           // 声明是外部变量( 函数 )

DLL1_API int subtract( int a, int b );        //声明是外部变量( 函数 )

 

class DLL1_APIPoint                        // 对于导出类的处理

{

     public:

            void output( int x, int y );

};

 

我们可以看到不仅仅是函数,甚至连类的名称都被改变咯!呵呵~对于C++客户端程序而言,这样的调用是没有问题的,但是对于其他的呢?例如C客户端,那显然是不行的!我们可以想到一个办法解决C客户端的问题:那就是但我们在定义宏的时候加上:extern “C” 是可以的:

          #define DLL1_API extern "C" _declspec( dllexport )

 

但是这是对于C语言的标准而言的额,对于其他的是不行的,例如对于WINWPI的标准 _stdcall还是不行的,自己可以尝试!

 

那么我们怎么解决呢!

 

我们就需呀-------à 模块定义文件(.def)

 

     一个简单实例:

LIBRARY  Dll2           

EXPORTS

add

subtract

 

一个.def文件中只有两个必需的部分:LIBRARY 和 EXPORTS。

第一行:''LIBRARY''是一个必需的部分。它告诉链接器(linker)如何命名你的DLL。

第二行:EXPORTS也是必须部分,下面每行分别是函数名。

 

此处只是简单介绍、、、具体的看 MSDN、、、

 

 

对于我们这样的操作,可以尝试一下,即使是其他的标准,例如:

int _stdcall add( int x, int y)               // WINAPI 标准

int _stdcall subtract( int x, int y )

 

最后导出的函数名也是 add 和subtract,不信自己尝试,O(∩_∩)O~

 

同样这样的操作对于“导出类”也是可以的哦~~~ 

 

 

3、  最后看一下动态加载一个 DLL 库

 

首先我们需要函数:LoadLibrary,其返回值是HINSTANCE

然后还有很重要的一点是:我们需要获取函数的地址,那么我们在此处的操作是利用函数指针来处理:typedef int (_stdcall *INSTANCEADD )( int a, int b ); // 此处我们处理add函数

 

代码如下:

 

HINSTANCE hInst;

hInst = LoadLibrary( "Dll2.dll");              // 动态加载Dll

typedef int ( _stdcall *INSTANCEADD )( int a, int b); 

INSTANCEADD ADD=( INSTANCEADD )GetProcAddress( hInst,MAKEINTRESOURCE( 1 ));     // 函数的ID可以在 dumpbin中看到

 

if ( !ADD)                 // if ADD 没有获取到函数的地址,那么返回值是0

{

       MessageBox("no address");

       return;

}

 

CString str;

str.Format( "5 + 3 = %d", ADD( 5, 3 ) ); // 调用外部函数( 注意此处是 ADD,不是 add )

MessageBox( str );

 

FreeLibrary( hInst);        // 释放 DLL

}

 

这些就是基础DLL编程,大侠们见笑咯~~~

转载地址:http://xoaci.baihongyu.com/

你可能感兴趣的文章
带WiringPi库的交叉笔译如何处理二之软链接概念
查看>>
Spring事务的七种传播行为
查看>>
ES写入找不到主节点问题排查
查看>>
Java8 HashMap集合解析
查看>>
欢迎使用CSDN-markdown编辑器
查看>>
Android计算器实现源码分析
查看>>
Android系统构架
查看>>
Android 跨应用程序访问窗口知识点总结
查看>>
各种排序算法的分析及java实现
查看>>
SSH框架总结(框架分析+环境搭建+实例源码下载)
查看>>
自定义 select 下拉框 多选插件
查看>>
js获取url链接携带的参数值
查看>>
gdb 调试core dump
查看>>
gdb debug tips
查看>>
linux和windows内存布局验证
查看>>
linux insmod error -1 required key invalid
查看>>
linux kconfig配置
查看>>
linux不同模块completion通信
查看>>
linux printf获得时间戳
查看>>
C语言位扩展
查看>>