Windows在加载DLL的时候,须要一个入口函数,就如同控制台或DOS程序须要main函数、WIN32程序须要WinMain函数同样。在前面的 例子中,DLL并无提供DllMain函数,应用工程也能成功引用DLL,这是由于Windows在找不到DllMain的时候,系统会从其它运行库中 引入一个不作任何操做的缺省DllMain函数版本,并不意味着DLL能够放弃DllMain函数。
根据编写规范,Windows必 须查找并执行DLL里的DllMain函数做为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意 味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。函数
咱们来看一个DllMain函数的例子 :线程
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved进程
)
{
switch (ul_reason_for_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_for_call指明了被 调用的缘由。缘由共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH, 以switch语句列出。
来仔细解读一下DllMain的函数头BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved )。
APIENTRY被定义为 __stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
进程中的每一个DLL模块被全局惟 一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄表明了DLL模块在进程虚拟空间中的起始地址。在Win32 中,HINSTANCE和HMODULE的值是相同的,这两种类型能够替换使用,这就是函数参数hModule的来历。it
执行下列代码:thread
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);bug
}
咱们看到输出顺序为:
process attach of dll
call add in dll:5
process detach of dll
这一输出顺序验证了DllMain被调用的时机。
代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接经过.def文件中为add函数指定的顺序号访问add函数,具体体如今MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个经过序号获取函数名的宏,定义为(节选自winuser.h):程序
#define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
#define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
#ifdef UNICODE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
#else
#define MAKEINTRESOURCE MAKEINTRESOURCEAcall