Create thread in Dll cannot execute

I have recently encountered a problem. When CThreadPool is initialized globally in the dll, the thread has been suspended and does not execute.
Find the answer in the following article, transferred from https://blog.csdn.net/qq_42021840/article/details/105956819:
The original words in Windows core programming say this:

 DLL 必须使用DllMain函数来对自己进行初始化。DllMain函数执行的时候,同一个地址空间的中的其他DLL可能还没有初始化,也就是没有调用其他DLL 的DllMain函数,所以我们应该尽量避免去使用从其他DLL中导入的函数。此外,还应该避免在DllMain中调用LoadLibrary(Ex)和FreeLibrary,因为这些函数可能会产生循环依赖。

For a better understanding, here is a brief introduction to the DllMain function:


BOOL WINAPI DllMain(

	_In_ HANDLE hInstance,

	_In_ ULONG  fdwReason,

	LPVOID Reserved

)                   //dll main 函数

{
    
    

	printf("%p\r\n", hInstance);

	switch (fdwReason)

	{
    
    

	case DLL_PROCESS_DETACH:  //0

	{
    
    

		break;

	}

	case DLL_PROCESS_ATTACH: //1

	{
    
    

		break;

	}

	case DLL_THREAD_ATTACH: //2

	{
    
    

		break;

	}

	case DLL_THREAD_DETACH: //3

	{
    
    

		break;

	}

	}

	return TRUE;

}

hInstance:该DLL示例的句柄。这个值表示一个虚拟的地址,DLL的文件映像就储存在这个位置。
fdwReason:表示调用入口点函数的原因/
Reserved:如果DLL是隐式加载的,那么该值不为零,否则为0

DLL_PROCESS_ATTACH 1
When the system maps a DLL to the address space of the process for the first time, it will call the DllMain function and pass in DLL_PROCESS_ATTACH in fdwReason. If after the first mapping, call LoadLibrary to load a DLL that has been mapped, the operating system will only increment the reference count of the DLL, and will not call DllMain.
A thread in the system must be responsible for executing the code in the DllMian function. When creating a new thread, the system allocates the process address space and maps the image of the .exe file into the process address space. Then, the system will create the main thread of the process, and use this main thread to call the DllMain function of each DLL, and pass in DLL_PROCESS_ATTACH at the same time. When all mapped DLLs have completed the call of DllMain, the system will let the main thread fetch the startup code of the C/C++ runtime that starts to execute the .exe, and then execute the entry point function (main or WinMain) of the .exe.

DLL_PROCESS_DETACH 0
When the system unmaps a DLL from the address space of the process, it will call the DllMain function and pass in DLL_PROCESS_DETACH in fdwReason. If the return is False when DLL_PROCESS_ATTACH, then there will be no notification of DLL_PROCESS_DETACH.
If the reason for unmapping is because the process is about to be terminated, then the thread that calls the ExitProcess function will be responsible for executing the DllMain function.
If the reason for unmapping is because a thread in the process called FreeLibrary, the calling thread will execute the code in the DllMain function. And the thread will not return until DllMian finishes processing the DLL_PROCESS_DETACH notification.
Note:
Dlls may prevent process termination. Only after each Dll has processed the DLL_PROCESS_DETACH notification, the operating system will terminate the process.
If the process terminates because of TerminateProcess, then the system will not call DllMian with DLL_PROCESS_DETACH.

DLL_THREAD_ATTACH 2
When a process creates a thread, the system will detect all DLL file images currently mapped to the process address space, and use DLL_THREAD_ATTACH to call the DllMain function of each DLL. The newly created thread is responsible for executing the code in the DllMain function of all DLLs. Only after all DLLs have finished processing the notification does the system let the new thread begin executing its thread code. (This is where the problem of thread deadlock occurs)
The newly created thread will only call the DllMain function in the DLL that has been mapped to the system process space. That is to say, when a DLL is mapped to the process address space, the existing thread will not call the DllMain function of the DLL.
Note:
The process will not let the main thread of the process call the DLL_THREAD_ATTACH value to call the DllMai function. When the process is created, any DLL mapped to the process address space will receive the DLL_PROCESS_ATTACH notification instead of the DLL_THREAD_ATTACH notification.

DLL_THREAD_DETACH 3
The preferred way when a thread terminates is to let its thread function return. This causes the system to call ExitThread to terminate the thread. ExitThread tells the system that the thread wants to terminate, but the system will not terminate immediately, but will let the thread use DLL_THREAD_DETACH to call the DllMain function of all mapped DLLs.
Note:
DLLs may interfere with the termination of threads. Only when each DLL has processed DLL_THREAD_DETACH, the system will actually terminate the thread.
A thread in the process calls LoadLibrary to load the DLL which causes the system to call the DLL's DllMain with DLL_PROCESS_ATTACH. When the thread that loaded the DLL exits, it calls the DllMain function with DLL_THREAD_DETACH.

After a brief understanding of the working mechanism of DllMain, let's analyze why a thread cannot be created in DllMain.

You first think about this situation:

一个进程有两个线程A和B,进程地址空间还映射了一个DLL.dll的DLL。两个线程都调用CreateThread来创建新的线程C和D。
当线程A调用CreateThread来创建线程C的时候,系统会用DLL_THREAD_ATTACH来调用DLL.dll中的DllMian函数,当新建线程C执行DllMain中的代码的时候,线程B调用CreateThread来创建线程D的时候,系统也必须调用DLL.dll中的DllMain函数,但是这次是让要让线程D来执行DllMain。
这个时候系统就会对DllMain函数的调用序列化,它会将线程D挂起,知道线程C执行完DllMain中的代码并返回为止。

This will cause the problem of thread deadlock caused by creating a thread in DllMain because the thread will be suspended and waited.

Take a look at the problematic code first:

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
    
    
	HANDLE ThreadHandle = NULL;
	DWORD  ThreadID = 0;
	switch (ul_reason_for_call)
	{
    
    
	case DLL_PROCESS_ATTACH:
	{
    
    
		setlocale(LC_ALL, "chinese");
		BOOL v1;
		MessageBox(0, _T("Dll加载成功"), 0, 0);
		
		ThreadHandle = CreateThread(NULL, 0, ThreadProcedure_1, NULL, 0, &ThreadID);
 
 
		WaitForSingleObject(ThreadHandle, INFINITE);
		CloseHandle(ThreadHandle);
		break;
	}
	case DLL_THREAD_ATTACH:
		MessageBox(0, _T("DllDLL_THREAD_ATTACH"), 0, 0);
 
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

Because:

  1. When DllMain receives DLL_PROCESS_ATTACH, a thread will be created. The system must use DLL_THREAD_ATTACH to call the DllMain function again. However, when the old thread creates a new thread, it will send a DLL_PROCESS_ATTACH notification to the DllMian of the new thread. Since the old thread has not initialized the DLL temporarily, that is, the call to DllMian has not been completed, the system will suspend the new thread until The old thread will wake up after completing the call. But the old thread called WaitForSingleObject to wait for the execution of the new thread. At this time, the new thread has been suspended and waited until the execution of the old thread is completed, but the old thread is also waiting for the completion of the execution of the new thread, so a deadlock situation occurs. Threads are waiting for each other's execution results.

The DisableThreadLibraryCalls function is mentioned in the "Windows Core Programming" book. This function prevents the system from sending DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications like the DllMain function of a specified DLL. In fact, this does not solve the problem.

Because:

When the system creates a process, it creates a lock at the same time. Each process has its own lock, multiple processes do not share the same lock. When a thread in the process calls the DllMain function mapped to the DLL in the process space, each thread will be synchronized through this lock.

When the program calls CreateThread, the system first creates the thread kernel object and thread stack. Then the system internally calls the WaitForSingleObject function, and passes in the mutex object handle of the process. When the new thread gets the ownership of the mutex, the system will let the new thread use DLL_THREAD_ATTACH to call the DllMain function of each DLL. Only then will the system call ReleaseMutex to relinquish the process' ownership of the mutex. Because of the way the system works, adding the DisableThreadLibraryCalls call doesn't prevent thread locks.

So the solution is just not to call WaitForSingleObject.

Guess you like

Origin blog.csdn.net/sinat_36391009/article/details/108764282