最近在写dll,看了一些网上的资源,自己整理了一下,这里把它贴出来。好了,进入正题。
我今天要讲的主要是非mfcdll,也就是win32 dynamic link library ,如下图所示:
图 1
1. 一个简单的dll示例
如图1所示,在file菜单下new一个个Win32 Dynamic-Link Library工程,取名为simple_dll。在建立的工程中添加如下文件
/* 文件名:simple_dll.h */
#ifndef SIMPLE_DLL_H
#define SIMPLE_DLL_H
extern "C" int _declspec(dllexport)add(int x, int y);
#endif
/* 文件名:simple_dll.cpp */
#include "simple_dll.h"
int add(int x, int y)
{
return x + y;
}
如上就建立了一个简单的动态链接库工程。按下F7,或者点击build菜单下的build simple_dll.dll可以在debug文件夹下生成simple_dll.lib和simple_dll.dll文件。
接下来我们编写一个win32 console application工程调用dll中的add函数。在文件菜单下点击新建,new一个win32 console application工程,如下图所示。 名称设为call_simple_dll。
然后把simple_dll工程中的simple_dll.lib和simple_dll.dll拷贝到call_simple_dll工程目下文件夹下。最后在call_simple_dll下新建main.cpp文件。
/**********文件main.cpp**************/
#pragma comment(lib,"simple_dll.lib")
#include<stdio.h>
//.lib 文件中仅仅是关于其对应DLL 文件中函数的重定位信息
extern "C" _declspec(dllimport) add(int x,int y);
int main(int argc, char* argv[])
{
int result = add(2,3);
printf("%d",result);
return 0;
}
调用成功,结果显示为5.
2. 调用dll中的函数
dll中的函数有两种调用方式,静态的和动态的。第1节中的例子属于静态调用。动态调用的方式如下:
按照第1节中的方式建立好工程,及main.cpp。具体代码改成如下:
#include <stdio.h>
#include <windows.h>
typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
{
HINSTANCE hDll; //DLL 句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("%d", result);
}
FreeLibrary(hDll);
}
return 0;
}
编译运行,可以得到和第1节中同样的结果。这里我们可以看到所谓动态调用就是使用了LoadLibrary——GetProcAddress——FreeLibrary三个windows API函数。静态调用和动态调用的区别在于,静态调用需要包含lib描述文件。客户端(即main函数)编译连接的时候,会把dll中该函数的地址绑定到main函数。这样,一旦dll发生改变,那么函数的地址也会发生改变。客户端也必须重新编译连接。而动态调用方式,因为使用后期动态绑定,连接的时候不需要知道函数的具体地址。只有在调用的时候,才进行连接。这样,无论dll中的函数怎么修改,反正在客户端调用时会对该函数的地址进行计算,所以即使dll发生改变,也不需要重新编译客户端程序。这就是动态调用和静态调用最主要的区别。当然大家也可以看到,静态调用代码要简洁一些。所以,如果dll功能固定的话,大家可以采用静态链接的方式。
3. dll中函数的导出
可以看到第1节中我们使用
extern "C" int _declspec(dllexport)add(int x, int y);
来声明add函数,这里_declspec(dllexport)的意思就是声明说明(declaration spec)add函数是一个dll导出函数。我们可以把simple_dll.dll用vc6 tools自带的depends工具查看, 如下图,我们可以看到add函数的入口指针为0x00001005 。这样说明将add符号才可以被外界调用。
4.dll中变量的导出
变量的导出方式跟函数基本一样
/*文件名: variable.h */
#ifndef _VARIABLE_H_
#define _VARIABLE_H_
#ifdef EXPORT
#define VARIABLE_DLL _declspec(dllexport)
#else
#define VARIABLE_DLL _declspec(dllimport)
#endif
extern VARIABLE_DLL int dllglobal;
#endif
/*文件名:variable.cpp */
#define EXPORT
#include "variable.h"
int dllglobal = 33;
在主函数中引用DLL 中定义的全局变量:
/* 文件名:main.cpp */
#include "variable.h"
#include<stdio.h>
#pragma comment(lib,"variable.lib")
int main()
{
printf("%d",dllglobal);
}
注意,这里需要将variable.h,variable.lib,variable.dll都拷贝到main.cpp同个文件夹下。否则编译不会成功。
5.dll中类的导出
下面的例子里,我们在DLL 中定义了point 和circle 两个类,并在应用工程中引用了它们。
//文件名:point.h,point 类的声明
#ifndef POINT_H
#define POINT_H
#ifdef EXPORT
#define CLASS_DLL _declspec(dllexport)
#else
#define CLASS_DLL _declspec(dllimport)
#endif
class CLASS_DLL point //导入类point{public:float y;float x;point();point(float x_coordinate, float y_coordinate);};
#endif
//文件名:point.cpp,point 类的实现
#define CLASS_DLL
#include "point.h"
//类point 的缺省构造函数
point::point()
{
x = 0.0;
y = 0.0
}
//类point 的构造函数
point::point(float x_coordinate, float y_coordinate)
{
x = x_coordinate;
y = y_coordinate;
}
客户端程序
#include "circle.h" //包含类声明头文件
#pragma comment(lib,"circle.lib");
int main()
{
circle c;
printf("area:%f girth:%f", c.x, c.y);
return 0;
}