VC++动态链接库(DLL)编程(一)――理解库
VC++
动态链接库(DLL)编程(二)--非MFC DLL
码中都包含了库工程和调用库的工程,这二者都被包含在一个工作区内,这是笔者提供这种打包下载的用意所在。
动态链接库中的导出接口可以使用Visual C++的Depends 工具进行查看,让我们用Depends 打开系统目录中的user32.d ll ,看到了吧?红圈内的就是几个版本的MessageBox 了!原来它真的在这里啊,原来它就在这里啊!
图5用Depends 查看DLL
当然Depends 工具也可以显示DLL 的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些DLL 。
好,让我们正式进入动态链接库的世界,先来看看最一般的DLL ,即非MFC DLL 。
{
switch(ul_reason_f or_call)
{
case DLL_PROCESS_ATTACH:
printf("\nprocess attach of dll");
break;
case DLL_THREAD_ATTACH:
printf("\nthread attach of dll");
break;
case DLL_THREAD_DETACH:
printf("\nthread detach of dll");
break;
case DLL_PROCESS_DETACH:
printf("\nprocess detach of dll");
break;
}
return TRUE;
}
DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DLLMain函数也被调用,
ul_reason_f or_call指明了被调用的原因。原因共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH,以switch语句列出。
来仔细解读一下DllMain的函数头BOOL APIENTRY DllMain(HANDLE hModule,WORD
ul_reason_f or_call,LPVOID lpReserved)。
APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI
方式;
进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。
执行下列代码:
hDll=LoadLibrary("..\\Debug\\dllTest.dll");
if(hDll!=NULL)
{
addFun=(lpAddFun)GetProcAddress(hDll,MAKEINTRESOURCE(1));
//MAKEINTRESOURCE直接使用导出文件中的序号
if(addFun!=NULL)
{
int result=addFun(2,3);
printf("\ncall add in dll:%d",result);
}
FreeLibrary(hDll);
}
我们看到输出顺序为:
process attach of dll
call add in dll:5
process detach of dll
这一输出顺序验证了DllMain被调用的时机。
代码中的GetProcAddress(hDll,MAKEINTRESOURCE(1))值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE(1),MAKEINTRESOURCE
Edited by Foxit Reader
Copyright(C) by Foxit Software Company,2005-2008 For Evaluation Only.
{
case DLL_PROCESS_ATTACH:
dllGlobalVar=100;//在dll被加载时,赋全局变量为100
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
;文件名:lib.def
;在DLL中导出变量
LIBRARY"dllTest"
EXPORTS
dllGlobalVar CONSTANT
;或dllGlobalVar DATA
GetGlobalVar
从lib.h和lib.cpp中可以看出,全局变量在DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def文件的EXPORTS后添加:
变量名CONSTANT//过时的方法
或
变量名DATA//VC++提示的新方法
在主函数中引用DLL中定义的全局变量:
#include
#pragma com ment(lib,"dllTest.lib")
extern int dllGlobalVar;
int m ain(int argc,char*argv[])
{
printf("%d",*(int*)dllGlobalVar);
*(int*)dllGlobalVar=1;
printf("%d",*(int*)dllGlobalVar);
return0;
}
特别要注意的是用extern int dllGlobalVar声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从*(int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操作:
dllGlobalVar=1;
其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。
在应用工程中引用DLL中全局变量的一个更好方法是:
#include
#pragma com ment(lib,"dllTest.lib")
extern int_declspec(dllimport)dllGlobalVar;//用_declspec(dllimport)导入
int m ain(int argc,char*argv[])
{
printf("%d",dllGlobalVar);
dllGlobalVar=1;//这里就可以直接使用,无须进行强制指针转换
//文件名:point.cpp,point类的实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#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;
}
//文件名:circle.h,circle类的声明
#ifndef CIRCLE_H
#define CIRCLE_H
#include"point.h"
#ifdef DLL_FILE
class_declspec(dllexport)circle//导出类circle #else
class_declspec(dllimport)circle//导入类circle #endif
{
public:
void SetCentre(const point¢rePoint);
void SetRadius(float r);
float GetGirth();
float GetArea();
circle();
private:
float radius;
point centre;
};
#endif
//文件名:circle.cpp,circle类的实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include"circle.h"
#define PI3.1415926
//circle类的构造函数
circle::circle()
{
centre=point(0,0);
radius=0;
}
//得到圆的面积
float circle::GetArea()
{
return PI*radius*radius;
}
//得到圆的周长
float circle::GetGirth()
{
return2*PI*radius;
}
//设置圆心坐标
void circle::SetCentre(const point¢rePoint) {
centre=centrePoint;