首先我们明白“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编程,大侠们见笑咯~~~