首页
社区
课程
招聘
[旧帖] [原创]DllMain函数必定是DLL被加载后第一个被调用的函数吗? 0.00雪花
发表于: 2010-10-8 15:50 2439

[旧帖] [原创]DllMain函数必定是DLL被加载后第一个被调用的函数吗? 0.00雪花

2010-10-8 15:50
2439
我们知道一个动态链接库一般由一个DllMain函数和若干导出函数组成。
比如对于Demo.DLL,含有DllMain以及导出函数FunctionA。当某个EXE使用该DLL时通常有两种方法,静态链接和动态链接。
1)静态链接
    需要.H和.LIB文件,访问时直接调用导出函数即可。DLL会随着EXE的加载而同时加载。
2)动态链接
    通过LoadLibrary和GetProcAddess,访问导出函数。

这时我们会问,两种方式下DllMain是在什么时机点被调用呢?
答案也很好分析及证实,如下:
1)静态链接
    Demo.DLL的DllMain调用时机在EXE的入口函数《WinMain》之前被调用。
2)动态链接
    Demo.DLL的DllMain调用时机在EXE的代码《LoadLibrary("Demo.DLL")》期间被调用。

到此为止,我们发现DllMain函数总是在导出函数被调用之前,就已经执行完毕。但是事实上有没有在一种情况下,导出函数调用时机比DllMain函数的调用时机还要早呢?
答案是肯定的,在某种特定的条件下,完全可以做到这种调用顺序。

举例如下:
1)存在EXE【Demo.EXE】以及两个DLL【DemoA.DLL】【DemoB.DLL】。
2)Demo.EXE静态链接这两个DLL,同时假定静态链接两个DLL的顺序为DemoA->DemoB。
3)DemoA中的DllMain中执行如下代码:
    LoadLibrary("DemoB"), GetProcAddess("DemoBFunctionB"),调用DemoBFunctionB。
这时会发现DemoB.DLL的导出函数的调用时机早于其DllMain函数的调用。
由此可以得出像以下的DLL设计构架是不一定能保证其正确性的。(在这摔过跟头,因此记忆犹新TAT)

//全局数据
DLLMain()
{
       //初始化全局数据
}
FunctionA()
{
       //使用全局数据
}

最后我们来分析一下其原因,这得从DLL的静态链接和动态链接的执行顺序来说起。
1)静态链接
   EXE会按照链接顺序,依次加载各个DLL到内存中,然后再依次调用各个DLL的DllMain函数。
在DLL被加载到内存之后,其后面的LoadLibrary仅仅是增加该DLL的引用计数而已。

2)动态链接
   EXE会按照编码顺序,依次加载各个DLL到内存中,并且在各DLL加载期间调用其DllMain函数。


这样由于Windows对于静态链接和动态链接的处理方式不同而引起的。至于为什么微软不设计成静态链接的情况下也在DLL被加载到内存之后,立即调用其DllMain函数就不得而知了。
个人觉得改善后的设计更好点,至少不会出现这种问题,或许也有我考虑不到的原因存在也说不定。

这篇文章的重点是通过问题,共享一下Windows下对于DLL的两种执行方式,注意红字部分噢

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (9)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持下支持下
2010-10-8 15:58
0
雪    币: 4560
活跃值: (1002)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
头次听说学习了
2010-10-8 15:59
0
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
本来就叫你不要在dllMain中调用LoadLibrary
2010-10-8 16:00
0
雪    币: 51
活跃值: (61)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
我之前写的dll从来没有dllmain
2010-10-8 16:02
0
雪    币: 37
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
文章真是寫的好
又學到一些知識
2010-10-8 21:22
0
雪    币: 83
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
用GetModuleHandle()也同样有问题。
在这种情况下,即使DemoB.DLL中的全局对象也不行。
比如说,在DemoB.DLL中定义全局变量CString g_Data;
在导出函数中执行赋值语句【g_Data = "Test"】之类的代码时,你突然会发现久违的Access deny的应用程序错和你见面了。因为此时CString对象根本还没有初始化。


那是你开发的框架为MFCDLL吧。这时候在你的theAPP对象的InitInstance函数中调用LoadLibrary或者GetModuleHandle时就要小心咯~
2010-10-8 22:18
0
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
你为什么一定要在dllMain里面执行LoadLibrary+GetProcAddress操作呢,这样本来就是不安全的。
2010-10-8 23:59
0
雪    币: 81
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
没有LZ这一调用,哪有这深层的内幕?
2010-10-9 06:40
0
雪    币: 83
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
何已见得这样一定是不安全的呢。
诚然,MSDN上明确说明不应该在DllMain中调用LoadLibrary,否则可能会出现循环引用的问题。
但是如果能知道为什么不应该调用岂不是更好。上述例子中就是引发循环引用的一个特例。知道了怎么回事后,就可以有避开这种特例的方法,毕竟研究为上。我所知道的部分商业程序中也不乏DllMain中调用LoadLibrary的处理,同时有时候为了实现某类功能也必须这么做。
2010-10-9 15:05
0
游客
登录 | 注册 方可回帖
返回
//