动态链接库DLL的创建和使用(2009-3-15 21:13:00)
(1) 新建工程DllDemo,工程类型选择“Win32 Dynamic-Link Library”
(2) 单击OK按钮,选择“A DLL that exports some symbols”,即要求VC++自动生成一些导出符号代码。
(3) 点击Finish,完成工程创建。
(4) 在头文件DllDemo.h中添加导出函数的声明。
//DllDemo.h
#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif
//如果要声明常量,直接使用#define
//声明要导出的函数
DLLDEMO_API void ExportFunc(LPCTSTR pszContent);
//声明要导出的类
class DLLDEMO_API MyDllClass
{
public:
MyDllClass();
};
//DllDemo.cpp
#include "stdafx.h"
#include "DllDemo.h"
#include <stdio.h>
//初始化全局变量
DLLDEMO_API int nDllExportVar=15;
HMODULE g_hModule; //全局模块句柄
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hModule=(HMODULE)hModule; //保存模块句柄
//case DLL_THREAD_ATTACH:
//case DLL_THREAD_DETACH:
//case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//要想将此函数导出供其他模块使用,须在DllDemo.h头文件中进行声明
void ExportFunc(LPCTSTR pszContent)
{
char sz[MAX_PATH];
//获得本DLL模块的文件名
::GetModuleFileName(g_hModule,sz,MAX_PATH);
::MessageBox(NULL,pszContent,strrchr(sz,'//')+1,MB_OK);
}
//自定义导出类
MyDllClass::MyDllClass()
{
::MessageBox(NULL,"DLL导出类","MyDllClass",MB_OK);
}
(6)编译链接程序。
/DllDemo/Release 文件夹下生成了动态链接库DllDemo.dll及其导入库DllDemo.lib。
以下为DLL函数查看器查看DllDemo.dll导出函数(类):
(7) 装载期间动态链接调用DllDemo.dll中定义的导出函数ExportFunc。
VC中使用DLL需要用到相应的lib文件和头文件。静态链接库lib文件中存放的是接口函数申明的入口地址,dll中存放的是函数实体!lib告诉编译器你的dll都导出了什么函数,以及这些函数的地址名称,运行的时候就根据这些信息到dll里面去找。头文件(*.h)作为一种包含功能函数、数据接口声明的载体文件,用于保存程序的声明(declaration),而定义文件(*.c|*.cpp)用于保存程序的实现 (implementation)。编译时需要用到头文件,链接时用到lib文件,运行exe时用到dll。以下为工程中使用动态链接库的步骤:
1、 将dll文件复制到工程的debug|release目录下,使其与exe文件放在一起。
Windows将遵循下面的搜索顺序来定位 DLL:包含EXE文件的目录>进程的当前工作目录(VC工程目录)>Windows系统目录(X :/WINDOWS/system(32))>Windows目录(X :/WINDOWS)>列在环境变量Path中的一系列目录.
2、 添加lib链接:先Project->Settings->Link->Object/library modules,填写you.lib,表示该工程将链接此静态库;再Tools->Options->Directories->Library Files,添加you.lib所在目录。链接时将到此目录查找lib文件。
VC将首先到安装目录的/VC98/LIB、/VC98/MFC/LIB目录(和自己添加的Library Files目录下)下查找需要的lib文件,然后再到当前工程目录下查找。查找顺序可在Tools->OptionsDirectories-> Library Files中进行调整。
3、 添加头文件:先在要使用该函数的地方包含该LIB的头文件,然后再Tools->Options->Directories->Header Files,添加you.h所在目录。链接时将到此目录查找头文件。
VC将首先到安装目录的/VC98/INCLUDE、/VC98/MFC/INCLUDE和/VC98/ATL/INCLUDE(和自己添加的Include Files目录下)查找编译系统提供的公共头文件<std***.h>。优先在工程目录下查找”MyHeader.h”,如果没有找到双引号头文件,再到以上编译系统公共头文件目录和自己添加的Include Files目录下查找。
查找顺序可在Tools->OptionsDirectories-> Header Files中进行调整。
新建一个DllApp的Win32控制台工程,然后按以上步骤添加DllDemo.h,DllDemo.lib,DllDemo.dll到DllApp工程。下面是调用导出函数ExportFunc的示例代码:
//DllApp.cpp
#include <stdio.h>
#include <windows.h>
#include "DllDemo.h"
//指明要链接到DllDemo.lib
//或直接Projects->Settings->Link添加DllDemo.lib到该项目
#pragma comment(lib,"DllDemo")
void main()
{
//像调用本地函数一样调用DllDemo.dll库的导出变量、函数、类
printf("DLL导出变量nDllExportVar=%d/n",nDllExportVar);
ExportFunc("DLL导出函数");
MyDllClass myDllClass;
}
(8)运行期间动态链接
所谓运行期间动态链接实在程序运行过程中显式地去加载DLL库,从中导出需要的函数。为了能够运行期间动态地导出函数,先为DllDemo工程建立一个DEF文件来指定要导出的函数。以下示例没有.lib文件,VC调用DLL中的函数。
//DllDemo.def
EXPORTS
ExportFunc
//以上两行说明此DLL库要向外导出ExportFunc函数,然后重新编译链接DllDemo工程。
现在将DllApp.cpp改成以下:
//DllApp.cpp
#include <windows.h>
//声明函数类型
typedef void (*PFNEXPORTFUNC)(LPCTSTR);
int main(int argc,char* argv[])
{
//加载DLL库
HMODULE hModule=::LoadLibrary("..//DllDemo//Release//DllDemo.dll");
if(hModule!=NULL)
{
//取得ExportFunc函数的地址
PFNEXPORTFUNC mExportFunc=(PFNEXPORTFUNC)::GetProcAddress(hModule,"ExportFunc");
if(mExportFunc!=NULL)
mExportFunc("Dll导出函数");
//MyDllClass myDllClass;这里将出错,静态加载后才能调用?
//卸载DLL库
::FreeLibrary(hModule);
}
return 0;
}
运行结果同(7)。
注意,对于一般的DLL就用LoadLibrary导入;如果DLL是COM的话就用import,例如我们在使用ADO操作数据库时,一般#import "C:/Program Files/Common Files/System/ADO/msado15.dll"。
如果只有dll文件,可通过Dll2Lib软件生成对应lib文件,再隐式链接。
(9)运行期间动态链接相对装载期间动态链接的优点是对于加载失败的情况能予以正确处理。而装载期间动态链接一旦丢失DLL文件则程序永远不能启动了。
//自定义导出函数 (5) 在实现文件DllDemo.cpp中定义导出函数。//声明要导出的变量
extern DLLDEMO_API int nDllExportVar;