一、DllMain 函数的使用。
BOOL WINAPI DllMain(
HANDLE hinstDLL, //保存dll的句柄
DWORD dwReason,
LPVOID lpvReserved
);
DllMain函数是DLL文件中一个可选的函数,当Dll文件中的某个函数需要使用当前dll的句柄时,就可以调用这个函数,同时可以在这个函数中进行简单的业务处理(注意不要提供太复杂的处理)。
二、DLL模块定义文件(.def文件)的使用。
由于在Dll中定义并导出的函数将要在调用dll的程序中被调用,那么由于编译器对函数名字改编,不同编译器改编之后的结果可能不同,这样就给函数的调用带来麻烦。同时为了使Dll能够在不同的程序语言中调用,就要使用模块定义文件来规范函数的导出名字。
三、关于Dll中函数的调用规则:1.C语言调用规则(C、C++语言的默认调用规则) 2.标准调用规则_stdcall(也就是PASCAL调用规则)——一般为了使dll可以为多种语言服务使用_stacall调用规则。
四、Dll的函数导入和导出。
在dll的CPP文件中函数定义前应该加上extern "C" _declspec(dllexport) _stdcall来说明函数是要导出的,并采用标准调用约定。在.h文件中的函数声明前加上extern "C" _declspec(dllimport) 函数返回类型 _stdcall(因为这个头文件是要给调用dll的客户程序使用的,所以是dllimport).为了方便书写可以采用下面的写法:
dll1.h文件头
#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport)
#endif
DLL1_API int _stdcall add(int a,int b);
dll1.cpp文件头
#define DLL1_API extern "C" _declspec(dllexport)
#include "Dll1.h"
int _stdcall add(int a,int b) //这里不必在int前添加DLL_API了
{
return a+b;
}
注意:关键字extern "C" 不能应用到类和类的成员函数中,如果想导出类应该使用模块定义文件导出。
五、Dll的加载:
1.静态加载(又称隐式加载):采用.Lib文件,这种方法简单方便,容易掌握,需要dll的头文件。在客户程序启动时将所有要用到的dll加载到内存中,这样就带来一个问题,如果客户程序需要调用很多个dll,而每个dll中只是调用很少的函数,那么会造成资源的浪费,使程序变慢。——这时应使用动态加载来解决这个问题。
2.动态加载(又称显示加载):不需要dll的头文件。在客户程序启动后,需要调用dll文件的函数时,在用函数加载dll,这样的效率比较高。下面是调用名字为Dll3的dll中add函数的方法示例:
HINSTANCE hInst;
hInst=LoadLibrary("Dll3.dll"); //将dll3.dll加载到内存,同时增加对其的引用计数,并保存dll的实例句柄。
typedef int (_stdcall *ADDPROC)(int a,int b); //定义函数指针,通过GetProcAddress得到add函数的地址
ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");//找到add函数的入口地址并保存在指针Add中
if(!Add)
{
MessageBox("获取函数地址失败!");
return;
}
CString str;
str.Format("5+3=%d",Add(5,3));
MessageBox(str);
FreeLibrary(hInst);//减少对dll文件引用计数