DLL(Dynamic Link Library,动态连接库)是一个可以被其他应用程序调用的应用模块,其中封装了可以被调用的资源或函数。DLL属于可执行文件,他符合Windows系统的PE文件格式,不过他的运行是依附于EXE文件创建的进程来执行的,不能单独运行。一个DLL文件可以被多个进程所装载调用。
DLL是动态连接库,相对应的,是静态连接库。动态连接库是在EXE文件运行时被加载执行的,而静态连接库时OBJ文件进行连接时同时被保存到程序中的。动态连接库可以减少可执行文件的体积,在需要的时候进入内存;将软件划分为多个模块,可以按照模块进行开发,对于发布与升级也非常方便。
DLL程序的编写与运行都有别于常见的命令行程序或Windows程序,无论是函数的入口还是其执行的方式都有差别。
1. DLL程序的入口不是main函数,也不是WinMain函数,而是DllMain函数,该函数的定义如下:
BOOLAPIENTRY DllMain(
HMODULEhModule,
DWORD ul_reason_for_call,
LPVOIDlpReserved
);
hModule:该参数是当前DLL模块的句柄,即本动态连接库模块的实例句柄。
ul_reason_for_call:该参数表示DllMain函数被调用的原因。
该参数有四种取值,分别为:DLL_PROCESS_ATTACH(当DLL被某进程装载时,调用DllMain);DLL_PROCESS_DETACH(当DLL被某进程卸载时,调用DllMain);DLL_THREAD_ATTACH(当进程中有线程被创建时,调用DllMain);DLL_THREAD_DETACH(当进程中有线程结束时,调用DllMain)。
lpReserved:保留参数,目前不被程序员所使用。
2.在VC6下创建一个DLL工程,步骤如下示:
文件 – > 新建-- > 进入“工程”选项卡。
工程类型选择:Win32Dynamic-Link Library,指定位置和工程名后点“确定”。
选择“一个简单的DLL工程”然后不断确定就行了。
生成的初始代码如下所示:
其中APIENTRY是一个函数修饰符,定义:#define APIENTRY WINAPI
由于DllMain函数不只一次地被调用,根据调用的情况不同,需要执行不同的代码。所以在编写DLL时,一般会将DllMain函数的结构写成如下形式:
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOIDlpReserved
)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: //动态库装载是调用
{
break;
}
case DLL_THREAD_ATTACH: //进程中有线程创建时调用
{
break;
}
case DLL_THREAD_DETACH: //进程中有线程结束时调用
{
break;
}
case DLL_PROCESS_DETACH: //动态库卸载是调用
{
break;
}
}
returnTRUE;
}
这样可以达到根据不同的调用原因执行不同的代码。
3.为DLL添加一个导出函数
对于DLL文件来说,DllMain并不是必须的。在StdAfx.h中添加导出函数的声明如下:
extern "C" __declspec(dllexport) VOIDMsgBox(char *szMsg);
在StdAfx.cpp中实现该函数如下:
VOID MsgBox(char *szMsg)
{
charszModuleName[MAX_PATH] = { 0 };
GetModuleFileName (NULL,szModuleName, MAX_PATH);
MessageBox (NULL,szMsg, szModuleName, MB_OK);
}
编译、链接后可发现对应debug目录下多出了一些文件,其中firstDLL.Dll和firstDLL.lib是我们后面要用到的。前者是DLL文件,包含编写的代码;后者是一个库文件,其中包含一些导出函数的相关信息。如下图示:
4. DLL程序无法单独运行,需要通过编写一个EXE程序来调用这个DLL文件中的导出函数。下面通过一个简单的程序来调用DLL。
右击“工作区”选择“添加工程到工作空间”。
按以下步骤创建工程:
在新创建的工程中添加一个新的cpp文件,并在其中输入以下代码:
#include<windows.h>
typedef VOID (*PFUNMSG) (char *);
int main()
{
HMODULE hMoudle =LoadLibrary("firstDLL.dll");
if(hMoudle == NULL)
{
MessageBox(NULL, "firstDLL.dll文件不存在!", "DLL文件加载失败", MB_OK);
return -1;
}
PFUNMSG pFunMsg =(PFUNMSG)GetProcAddress(hMoudle, "MsgBox");
pFunMsg("Hello First Dll!");
return 0;
}
编译、链接、并运行会产生如下结果:
原因是该工程下还没有firstDLL.dll文件,我们把该文件拷到新添加的工程目录下,如下:
再重新链接、运行后可以看到结果:
此时,我们的第一个DLL调用程序就成功了。是不是迫不及待想试一试了呢。
源码可在以下地址中获得:
http://pan.baidu.com/s/1b1xH2A