现今的程序在大部分情况下,都或多或少引用了一些功能库。因此了解动态库DLL的加载和初始化,有利于避免一些依赖问题的出现。
动态库的加载有 动态加载 和静态加载两种方式。现仅对静态加载的方式进行一些跟踪,来得出一些顺序上的结果。
程序主程入口:int main(char argc, char *argv[]);
DLL库的入口 :BOOL WINAPI DllMain(DWORD dwReason, LPVOID /*lpReserved*/);
注,
1),如果 DLL 没有声明自己的入口函数:DllMain,系统会调用一个缺省入口函数:DllMain
2),如果程序过程中创建新子线程,同样会调用入口DLL 入口函数:DllMain
3),如果强制终止 进程/线程,则不会调用入口函数:DllMain(所以在某些情况下强制终止线程会出现内存泄漏的情况)
DLL的跟踪代码
class CGlobalT
{
public:
CGlobalT()
{
printf("CGlobalT constructor called\n");
}
~CGlobalT()
{
printf("~CGlobalT destructor called\n");
}
protected:
private:
};
static CGlobalT m_global;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("DLL process attached\n");
break;
case DLL_THREAD_ATTACH:
printf("DLL thread attached");
break;
case DLL_THREAD_DETACH:
printf("DLL thread detached");
break;
case DLL_PROCESS_DETACH:
printf("DLL process detached");
break;
}
return TRUE;
}
创建了DLL入口函数:DllMain()、静态全局变量:m_global
通过跟踪全局对象的构造函数调用和接口调用,来确定调用顺序。
主程的跟踪代码
#pragma comment(lib,"..\\..\\build\\dllTest.lib")
#include "..\..\dllTest\dllTest\dllTest.h"
#include "ThreadInterface.h"
class CMainGlobal
{
public:
CMainGlobal()
{
printf("CMainGlobal constructor called\n");
}
~CMainGlobal()
{
printf("~CMainGlobal destructor called\n");
}
protected:
private:
};
static CMainGlobal m_mainGlobal;
int main(int argc, char *argv[])
{
func();
CExportClass exportClass;
exportClass.DoAction();
CThreadInterface threadC;
threadC.StartThread();
Sleep(500);
threadC.ExistThread();
return EXIT_SUCCESS;
}
创建了主程入口:main() 和静态全局变量:m_mainGlobal
同样通过跟踪全局对象的 构造函数调用和 mian调用,来确定调用顺序
跟踪得到的调用顺序
1)DLL 全局变量 constructor 调用
2)DllMain()入口函数调用 :DLL_PROCESS_ATTACH
3)main 全局变量 constructor 调用
4)main() 函数调用
1)DLL 全局变量 constructor 调用
2)DllMain()入口函数调用 :DLL_PROCESS_ATTACH
3)main 全局变量 constructor 调用
4)main() 函数调用
5)DllMain()入口函数调用:DLL_THREAD_ATTACH.(ThreadC 为线程对象,启动线程后的动作)
6)DllMain()入口函数调用:DLL_THREAD_DETACH
7)main()函数退出
8)main() 全局变量 destructor 调用
9)DllMain()入口函数调用:DLL_PROCESS_DETACH
10)DLL 全局变量 destructor 调用
6)DllMain()入口函数调用:DLL_THREAD_DETACH
7)main()函数退出
8)main() 全局变量 destructor 调用
9)DllMain()入口函数调用:DLL_PROCESS_DETACH
10)DLL 全局变量 destructor 调用
通过以上跟踪的顺序得出的一些结论:
1)程序加载静态DLL的动作:全局变量初始化、dll入口调用,均先于main全局静态变量加载
2)加载DLL过程和主程调用过程,都是先初始化全局变量后调用入口函数
3)接口退出和DLL卸载过程刚好相反,调用带入的参数不一样