问题1:请问使用umdh分析内存泄漏时最重要的注意事项是什么(是不是我哪个地方的设置不对,或符号文件的事)?---之所以这么问是因为~我感觉分析出来的结果(栈回溯)一部分看起来不太准确,例如我的exe并没有调用一个第三库的函数(接口),却显示出这个库中有内存泄漏。。。
问题2:如果我的程序中有一个切换的逻辑,也就是说因为可能会出现“申请-释放-申请....”,(当然这种机制下我们使用了内存池),在什么时机用umdh抓取内存状态会更合适呢?因为我发现我隔几分钟抓取内存的对比结果都不太一样。例如:一个小时间抓一个内存状态1.txt,再过1个小时之后,抓一个内存状态2.txt,对比较这两个文件发现总共(total)涨了1M的内存,但是!!再过1分钟我再抓内存,发现变成降了100k的内存了,(从任务管理器内看虚拟内存和专用工作集都是涨的状态),这是怎么回事啊?是不是我抓的时机不对,是不是应该把程序停下来再抓?
问题3:用umdh对比出来的内存泄漏为什么与任务管理器中显示的不太一样呢,如果对比发现总共涨了1M,任务管理器中看到却是2M或者更多(虚拟内存和专用工作集)。。问题是任务管理器中哪个列(虚拟内存or专用工作集or“其它”)的涨幅才是真正的能跟umdh中显示的对应一致上呢?
-------我的问题很多,非常感谢能够帮忙我们的人,虽然我们不曾见面,也没有报酬,但我想有很多默默伸出援手的人在这里,我们会永远地记住帮忙过我们的人,祝您一生快乐、平安。
不怕问题多,但是要精确,因为泛泛而谈可能张冠李戴,毫无意义......
拿出一个例子,copy, paste实际数据...
下面是我们在一个项目中使用umdh抓取的栈回溯信息
+ 650160 ( 1160880 - 510720) 13820 allocs BackTrace31A74AA0+ 7740 ( 13820 - 6080) BackTrace31A74AA0 allocations
ntdll!EtwSetMark+0000244D KERNELBASE!LocalAlloc+00000058 nslook!CNoTrackObject::operator new+00000010 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 78) nslook!CThreadLocal<AFX_MODULE_THREAD_STATE>::CreateObject+00000007 (f:\dd\vctools\vc7libs\ship\atlmfc\include\afxtls_.h, 198) nslook!CThreadLocal<AFX_MODULE_THREAD_STATE>::GetData+0000000A (f:\dd\vctools\vc7libs\ship\atlmfc\include\afxtls_.h, 178) nslook!__DllMainCRTStartup+0000006C (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 318) nslook!_DllMainCRTStartup+0000001E (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 281) ntdll!wcsncmp+0000004C ntdll!LdrShutdownThread+000000E2 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!_callthreadstartex+00000059 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 650160 ( 1160880 - 510720) 13820 allocs BackTrace31A72D8C+ 7740 ( 13820 - 6080) BackTrace31A72D8C allocations
ntdll!EtwSetMark+0000244D KERNELBASE!LocalAlloc+00000058 NVDSDK!DEC_ClientSetMsgCallback+000179EE NVDSDK!DEC_ClientSetMsgCallback+00017380 NVDSDK!DEC_ClientSetMsgCallback+00017454 NVDSDK!DEC_ClientSetMsgCallback+00022A2E NVDSDK!DEC_ClientSetMsgCallback+00022AD6 ntdll!wcsncmp+0000004C ntdll!LdrShutdownThread+000000E2 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!_callthreadstartex+00000059 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 123840 ( 221056 - 97216) 13816 allocs BackTrace31A749B8+ 7740 ( 13816 - 6076) BackTrace31A749B8 allocations
ntdll!EtwSetMark+0000244D KERNELBASE!LocalAlloc+00000058 nslook!CNoTrackObject::operator new+00000010 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 78) nslook!CThreadSlotData::SetValue+0000007C (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 291) nslook!CThreadLocalObject::GetData+00000080 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 432) nslook!AfxGetModuleState+0000000F (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxstate.cpp, 456) nslook!__DllMainCRTStartup+0000006C (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 318) nslook!_DllMainCRTStartup+0000001E (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 281) ntdll!wcsncmp+0000004C ntdll!LdrShutdownThread+000000E2 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!_callthreadstartex+00000059 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 123840 ( 221104 - 97264) 13819 allocs BackTrace31A72CA4+ 7740 ( 13819 - 6079) BackTrace31A72CA4 allocations
ntdll!EtwSetMark+0000244D KERNELBASE!LocalAlloc+00000058 NVDSDK!DEC_ClientSetMsgCallback+000179EE NVDSDK!DEC_ClientSetMsgCallback+00017E3F NVDSDK!DEC_ClientSetMsgCallback+00018086 NVDSDK!DEC_ClientSetMsgCallback+00017969 NVDSDK!DEC_ClientSetMsgCallback+00022A2E NVDSDK!DEC_ClientSetMsgCallback+00022AD6 ntdll!wcsncmp+0000004C ntdll!LdrShutdownThread+000000E2 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!_callthreadstartex+00000059 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 92880 ( 165816 - 72936) 13818 allocs BackTrace31A72D38+ 7740 ( 13818 - 6078) BackTrace31A72D38 allocations
ntdll!EtwSetMark+0000244D KERNELBASE!LocalAlloc+00000058 NVDSDK!DEC_ClientSetMsgCallback+00017E33 NVDSDK!DEC_ClientSetMsgCallback+00018086 NVDSDK!DEC_ClientSetMsgCallback+00017969 NVDSDK!DEC_ClientSetMsgCallback+00022A2E NVDSDK!DEC_ClientSetMsgCallback+00022AD6 ntdll!wcsncmp+0000004C ntdll!LdrShutdownThread+000000E2 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!_callthreadstartex+00000059 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 92880 ( 165828 - 72948) 13819 allocs BackTrace31A74A4C+ 7740 ( 13819 - 6079) BackTrace31A74A4C allocations
ntdll!EtwSetMark+0000244D KERNELBASE!LocalAlloc+00000058 nslook!CThreadSlotData::SetValue+00000070 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 310) nslook!CThreadLocalObject::GetData+00000080 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 432) nslook!AfxGetModuleState+0000000F (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxstate.cpp, 456) nslook!__DllMainCRTStartup+0000006C (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 318) nslook!_DllMainCRTStartup+0000001E (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 281) ntdll!wcsncmp+0000004C ntdll!LdrShutdownThread+000000E2 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!_callthreadstartex+00000059 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
这是内存涨得最多的几个栈信息,都是调到了我们项目中的一个库PlaysdkM4调到了ntdll 再从ntdll调回我们项目中没有使用的库,很奇怪。
而且更匪夷所思的是_beginthreadex这个函数涨内存,上网查了可能是和tiddata结构体有关,我们还特地在线程中直接返回0并在线程退出前显式的调用_endthreadex(0)内存仍然涨在这。
上面贴的栈是没有调_endthreadex的
下面是我刚刚抓的显示调用_endthreadex(0)umdp 2小时后对比的结果,和上面几乎一样,nslook和nvdsdk在老化过程总没用逻辑调用到。而且更不可能从ntdll调上来。
+ 6436080 ( 7081200 - 645120) 84300 allocs BackTrace2E883B60+ 76620 ( 84300 - 7680) BackTrace2E883B60 allocations
ntdll!RtlAllocateHeap+00000274 KERNELBASE!LocalAlloc+0000005F nslook!CNoTrackObject::operator new+00000010 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 78) nslook!CThreadLocal<AFX_MODULE_THREAD_STATE>::CreateObject+00000007 (f:\dd\vctools\vc7libs\ship\atlmfc\include\afxtls_.h, 198) nslook!CThreadLocal<AFX_MODULE_THREAD_STATE>::GetData+0000000A (f:\dd\vctools\vc7libs\ship\atlmfc\include\afxtls_.h, 178) nslook!__DllMainCRTStartup+0000006C (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 318) nslook!_DllMainCRTStartup+0000001E (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 281) ntdll!LdrpCallInitRoutine+00000014 ntdll!LdrShutdownThread+000000E6 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!PlayThreadtest+0000000A (f:\windows sdk\windows\playsdkm4\trunk\source\sourcecode\player\player.cpp, 669) PlaySdkM4!_callthreadstartex+00000053 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 6436080 ( 7081200 - 645120) 84300 allocs BackTrace2E8819CC+ 76620 ( 84300 - 7680) BackTrace2E8819CC allocations
ntdll!RtlAllocateHeap+00000274 KERNELBASE!LocalAlloc+0000005F NVDSDK!DEC_ClientSetMsgCallback+000179EE NVDSDK!DEC_ClientSetMsgCallback+00017380 NVDSDK!DEC_ClientSetMsgCallback+00017454 NVDSDK!DEC_ClientSetMsgCallback+00022A2E NVDSDK!DEC_ClientSetMsgCallback+00022AD6 ntdll!LdrpCallInitRoutine+00000014 ntdll!LdrShutdownThread+000000E6 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!PlayThreadtest+0000000A (f:\windows sdk\windows\playsdkm4\trunk\source\sourcecode\player\player.cpp, 669) PlaySdkM4!_callthreadstartex+00000053 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 1225728 ( 1348576 - 122848) 84286 allocs BackTrace2E883A38+ 76608 ( 84286 - 7678) BackTrace2E883A38 allocations
ntdll!RtlAllocateHeap+00000274 KERNELBASE!LocalAlloc+0000005F nslook!CNoTrackObject::operator new+00000010 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 78) nslook!CThreadSlotData::SetValue+0000007C (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 291) nslook!CThreadLocalObject::GetData+00000080 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 432) nslook!AfxGetModuleState+0000000F (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxstate.cpp, 456) nslook!__DllMainCRTStartup+0000006C (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 318) nslook!_DllMainCRTStartup+0000001E (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 281) ntdll!LdrpCallInitRoutine+00000014 ntdll!LdrShutdownThread+000000E6 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!PlayThreadtest+0000000A (f:\windows sdk\windows\playsdkm4\trunk\source\sourcecode\player\player.cpp, 669) PlaySdkM4!_callthreadstartex+00000053 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 1225728 ( 1348576 - 122848) 84286 allocs BackTrace2E8818A4+ 76608 ( 84286 - 7678) BackTrace2E8818A4 allocations
ntdll!RtlAllocateHeap+00000274 KERNELBASE!LocalAlloc+0000005F NVDSDK!DEC_ClientSetMsgCallback+000179EE NVDSDK!DEC_ClientSetMsgCallback+00017E3F NVDSDK!DEC_ClientSetMsgCallback+00018086 NVDSDK!DEC_ClientSetMsgCallback+00017969 NVDSDK!DEC_ClientSetMsgCallback+00022A2E NVDSDK!DEC_ClientSetMsgCallback+00022AD6 ntdll!LdrpCallInitRoutine+00000014 ntdll!LdrShutdownThread+000000E6 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!PlayThreadtest+0000000A (f:\windows sdk\windows\playsdkm4\trunk\source\sourcecode\player\player.cpp, 669) PlaySdkM4!_callthreadstartex+00000053 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 919332 ( 1011456 - 92124) 84288 allocs BackTrace2E881938+ 76611 ( 84288 - 7677) BackTrace2E881938 allocations
ntdll!RtlAllocateHeap+00000274 KERNELBASE!LocalAlloc+0000005F NVDSDK!DEC_ClientSetMsgCallback+00017E33 NVDSDK!DEC_ClientSetMsgCallback+00018086 NVDSDK!DEC_ClientSetMsgCallback+00017969 NVDSDK!DEC_ClientSetMsgCallback+00022A2E NVDSDK!DEC_ClientSetMsgCallback+00022AD6 ntdll!LdrpCallInitRoutine+00000014 ntdll!LdrShutdownThread+000000E6 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!PlayThreadtest+0000000A (f:\windows sdk\windows\playsdkm4\trunk\source\sourcecode\player\player.cpp, 669) PlaySdkM4!_callthreadstartex+00000053 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
+ 919308 ( 1011444 - 92136) 84287 allocs BackTrace2E883ACC+ 76609 ( 84287 - 7678) BackTrace2E883ACC allocations
ntdll!RtlAllocateHeap+00000274 KERNELBASE!LocalAlloc+0000005F nslook!CThreadSlotData::SetValue+00000070 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 310) nslook!CThreadLocalObject::GetData+00000080 (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxtls.cpp, 432) nslook!AfxGetModuleState+0000000F (f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\afxstate.cpp, 456) nslook!__DllMainCRTStartup+0000006C (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 318) nslook!_DllMainCRTStartup+0000001E (f:\dd\vctools\crt_bld\self_x86\crt\src\dllcrt0.c, 281) ntdll!LdrpCallInitRoutine+00000014 ntdll!LdrShutdownThread+000000E6 ntdll!RtlExitUserThread+0000002A PlaySdkM4!_endthreadex+0000004A (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 414) PlaySdkM4!PlayThreadtest+0000000A (f:\windows sdk\windows\playsdkm4\trunk\source\sourcecode\player\player.cpp, 669) PlaySdkM4!_callthreadstartex+00000053 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 348) PlaySdkM4!_threadstartex+000000A4 (f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c, 331)
每个DLL都可以有一个入口点函数DllMain,系统会在不同的时刻调用此函数。以下是DllMain的一般形式:
BOOL WINAPI DllMain( HINSTANCE hinstDLL, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID lpReserved ) // reserved { // Perform actions based on the reason for calling. switch( fdwReason ) { case DLL_PROCESS_ATTACH: // Initialize once for each new process. // Return FALSE to fail DLL load. break; case DLL_THREAD_ATTACH: // Do thread-specific initialization. break; case DLL_THREAD_DETACH: // Do thread-specific cleanup. break; case DLL_PROCESS_DETACH: // Perform any necessary cleanup. break; } return TRUE; // Successful DLL_PROCESS_ATTACH. }
以上代码摘自MSDN,几乎所有的DllMain都以这种形式呈现。
先来看一下这个函数传递进来的参数:
1、 HINSTANCE hinstDLL
这个参数是该DLL实例的句柄,也就是此DLL映射到进程地址空间后,在该进程地址空间中的位置。
2、 DWORD fdwReason
此参数标示了调用DllMain函数的原因。有四种值,就是函数中case后的取值。各个取值的含义,稍后论述。
3、 LPVOID lpReserved
保留。
现在我们来讨论一下fdwReason的四种取值,这些取值,也直接反映了操作系统会在何种情况下调用DllMain。
1、DLL_PROCESS_ATTACH
当系统第一次将一个DLL映射到进程地址空间中时,会调用DllMain,并为fdwReason传入DLL_PROCESS_ATTACH。
注意,只有在第一次映射的时候,才会这样。如之后,另一线程再次显式加载此DLL,则操作系统只是增加该DLL的使用计数,而不会再次使用DLL_PROCESS_ATTACH来调用DllMain。
对DLL_PROCESS_ATTACH的处理,代表了DLL的初始化。
DllMain的返回值,也是针对DLL_PROCESS_ATTACH消息的。对于其余的三种取值,不起作用。
对于隐式加载,如DllMain返回FALSE,则程序会启动失败。对于显式加载,则会使LoadLibrary返回NULL。
2、DLL_PROCESS_DETACH
当系统将一个DLL从进程地址空间中撤销映射时,则会向DllMain传入DLL_PROCESS_DETACH。我们应当在此处放置一些清理代码。
当使用FreeLibrary时,如该线程的使用计数为0时,操作系统才会使用DLL_PROCESS_DETACH来调用DllMain。如使用计数大于0,则只是单纯的减少该DLL的计数。
3、DLL_THREAD_ATTACH
当进程创建一个线程,则系统会检查当前已映射到该进程空间中的所有DLL映像,并用DLL_THREAD_ATTACH来调用每个DLL的DllMain。
只有当所有DLL都完成了对DLL_THREAD_ATTACH的处理后,新线程才会执行它的线程函数。
另外,主线程不可能用DLL_THREAD_ATTACH来调用DllMain,因为主线程必然是在进程初始化的时候,用DLL_PROCESS_ATTACH调用DllMain的。
4、DLL_THREAD_DETACH
线程若要终止,会调用ExitThread,但是此函数不会立即终止线程,而是会利用DLL_THREAD_DETACH来调用当前进程地址空间中的所有DLL镜像的DllMain.
当每个DLL的DllMain都处理完后,系统才会真正的结束线程。
最后看一下DllMain的序列化调用
举个例子:
进程中有两个线程,A与B。进程的地址空间中,映射了一个名为SomeDll.dll的DLL。两个线程都准备通过CreateThread来创建另两个线程,C和D。
当线程A调用CreateThread来创建线程C的时候,系统会用DLL_THREAD_ATTACH来调用SomeDll.dll的DllMain,当线程C执行其中代码的时候,线程B调用CreateThread来创建线程D。
这时,系统同样会用DLL_THREAD_ATTACH来调用SomeDll.dll的DllMain,这次是让线程D来执行其中的代码。
但是此时,系统会对DllMain执行序列化,它会将线程D挂起,直至线程C执行完DllMain中的代码返回为止。
当C线程执行完DllMain中的代码并返回时,可以继续执行C的线程函数。此时,系统会唤醒线程D,让D执行DllMain中的代码。当返回后,线程D开始执行线程函数。
张老师,现在的疑惑是nslook和nvdsdk都没有显式的dllmain函数,DLL_THREAD_ATTACH和DLL_THREAD_DETACH是怎样的?
如果用户代码没有写DllMain,那么编译器会使用dummy的DllMain,CRT源代码中可以找到:
比如C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src\
BOOL WINAPI DllMain( HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved ){#if defined (CRTDLL) if ( dwReason == DLL_PROCESS_ATTACH && ! _pRawDllMain ) DisableThreadLibraryCalls(hDllHandle);#endif /* defined (CRTDLL) */ return TRUE ;}
BOOL WINAPI _DllMainCRTStartup( HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved ){
// ...
// invoke DllMain();
if ( (dwReason == DLL_PROCESS_DETACH) || (dwReason == DLL_THREAD_DETACH) ) { if ( _CRT_INIT(hDllHandle, dwReason, lpreserved) == FALSE ) retcode = FALSE ;// ...
}
建议使用WinDBG附加到目标进程,跟踪一下就清楚了...