搜档网
当前位置:搜档网 › VC++动态链接库(DLL)编程

VC++动态链接库(DLL)编程

VC++动态链接库(DLL)编程
VC++动态链接库(DLL)编程

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;

相关主题