DLLMain的入口函数不能创建新线程

来源:互联网 发布:python asynchat 编辑:程序博客网 时间:2024/05/01 19:34

微软官方有解释:

PRB: Cannot Create an MFC Thread During DLL Startup

SYMPTOMS

An MFC DLL that creates a new MFC thread during startup hangs when loaded by an application. This includes whenever a thread is created by calling AfxBeginThread or CWinThread::CreateThread inside:

the InitInstance of a CWinApp-derived object in a Regular DLL.
a supplied DllMain or RawDllMain function in a Regular DLL.
a supplied DllMain or RawDllMain function in an Extension DLL.

CAUSE

For Regular DLLs, which have a CWinApp-derived object, the CWinApp::InitInstance override is called from MFC's supplied DllMain when a process is attaching to the DLL. That is, DllMain is entered with a Reason For Call of DLL_PROCESS_ATTACH, and in handling this, MFC calls in to InitInstance before cleaning up and leaving DllMain.

For Extension DLLs, the startup of the DLL is the same, except Extension DLLs do not have a CWinApp-derived object and therefore have no InitInstance.

Whenever new threads are created that use code in the DLL, DllMain is called with a Reason For Call of DLL_THREAD_ATTACH to announce to the DLL that a new thread is attaching to it. If a new thread is created in the InitInstance of an MFC Regular DLL or in the DllMain of any MFC DLL during DLL_PROCESS_ATTACH, this second thread will attempt to re-enter the DllMain, which has not yet been exited from in an effort to announce DLL_THREAD_ATTACH.

DllMain, however, is not re-entrant. That means that the second thread will not start executing until the initial creating thread has finished its work in InitInstance, returned to and left DllMain.

In versions of MFC included with 32-bit Visual C++ versions 2.2 and earlier, MFC allowed threads to be created during startup, and usually DLLs that did this would work acceptably. The second thread would get created without problem but would not start executing until the first thread left DllMain. However, it has never been a good idea to create threads in DllMain during DLL_PROCESS_ATTACH.

As of MFC 4.0, the CWinThread::CreateThread function, which is called by AfxBeginThread, now waits on the created thread to start up and initialize MFC specific data before it returns control to the calling thread. Because the second thread is waiting on the first thread to leave DllMain, both threads crash headlong into deadlock and the application hangs.

Note that this information is equally valid for the RawDllMain function. Moreover, MFC DLLs should not use a RawDllMain function at all.

RESOLUTION

Regular DLLs that create threads should only do so in functions exported from the DLL and called by client applications. Furthermore, no MFC DLL -- neither Extension nor Regular -- should create an MFC thread in the DllMain or RawDllMain function. This ensures that the thread will not be created in the middle of any critical startup code.

The recommended solution for MFC DLLs that need to create a thread when the DLL starts is to add a specific exported initialization function and create the thread in it. Applications that use the DLL would need to call this function sometime during startup, most likely during the application 's InitInstance if it uses MFC. Or, if the application is loading the DLL explicitly, the application should call the initialization function immediately after the call to load the library.

The practice of exporting an initialization function for a DLL is not uncommon. Nevertheless, there may be situations where DLLs created with earlier versions of MFC are being ported but the client application cannot be changed to include a call to an initialization function. The alternative to an initialization function is to create the thread in one of the pre- existing exported functions. Any of the DLL 's exported functions that require a running thread should be responsible for first checking to see if that thread exists and then creating it if it does not.

STATUS

This behavior is by design.

就是不建议在 DllMain 中的 DLL_PROCESS_ATTACH 情况下去创建新线程可以把一个函数导出去,让exe在外部去调用,这是个不错的方法
DllMain()中不要干复杂的事情,因为持有loader锁时干复杂事情很容易死锁。

可以让dll导出一对Init()/CleanUp()的函数,用于做全局初始化(比如创建线程)、全局清理(比如等待线程退出,不等待线程退出就会非法操作)。

而且DLL要返回后线程才会开始执行

DLL中还严忌使用使用等待函数

CreateThread也建议换成_beginthreadex,
_beginthreadex实际也是调用CreateThread,把它包装而已。在DLL中创建的线程,必须在DLL unload之前等待其退出,不然线程体对应的代码区域被unload了,就崩了至于原理,那还真是要补一下《windows核心编程》,研究一下死锁跟线程了。



0 0
原创粉丝点击