The common practice of using TLS (local thread storage) in DLL is to allocate memory for each thread when the dll_process_attach/dll_thread_attach in dllmain is called, the memory is released when dll_thread_detach/dll_process_detach is called. This sample code is available in the msdn article using thread local storage in a dynamic-Link Library.
Bool winapi dllmain (hinstance hinstdll, // DLL module handle
DWORD fdwreason, // reason called
Lpvoid lpvreserved) // Reserved
{
Lpvoid lpvdata;
Bool fignore;
Switch (fdwreason)
{
Case dll_process_attach:
// Allocate a TLS index.
If (dwtlsindex = tlsalloc () = tls_out_of_indexes) return false;
Case dll_thread_attach:
Lpvdata = (lpvoid) localalloc (lptr, 256); // allocate memory for each thread
If (lpvdata! = NULL)
Fignore = tlssetvalue (dwtlsindex, lpvdata );
Break;
Case dll_thread_detach:
Lpvdata = tlsgetvalue (dwtlsindex );
If (lpvdata! = NULL)
Localfree (hlocal) lpvdata); // release the memory
Break;
Case dll_process_detach:
Lpvdata = tlsgetvalue (dwtlsindex );
If (lpvdata! = NULL)
Localfree (hlocal) lpvdata); // release the memory
Tlsfree (dwtlsindex );
Break;
Default:
Break;
}
Return true;
}
This Code considers dll_thread_detachAlwaysBut this is not the case. In some cases, dll_thread_detach andNoIs called, resulting in Memory leakage. Next we will conduct two simple experiments to illustrate this problem.
Lab code:
Typedef void (_ stdcall * fnsleep )();
Void calltestdll ()
{
Fnsleep pfnsleep = (fnsleep): getprocaddress (g_hdllmodule, "dosleep ");
Atlassert (pfnsleep );
(* Pfnsleep )();
}
DWORD winapi threadproc (lpvoid lpparam)
{
Calltestdll ();
Return 0;
}
G_hdllmodule =: loadlibrary (_ T ("testdll. dll "));
Atltrace ("[thread % d] loadlibrary = 0x %. 8x/N",: getcurrentthreadid ());
Calltestdll ();
Const int max_thread = 2;
Handle hthread [max_thread];
For (INT I = 0; I <max_thread; I ++)
{
Hthread [I] =: createthread (null, 0, threadproc, 0, 0, null );
}
Sleep (max_thread * 1000 );
: Freelibrary (g_hdllmodule );
Output result 1:
[Thread 4976] dll_process_attach // main thread
[Thread 4976] loadlibrary = 0x0ecbf9d4
[Thread 4976] dosleep () in DLL
[Thread 7860] The thread generated by dll_thread_attach // createthread
[Thread 736] The thread generated by dll_thread_attach // createthread
[Thread 736] dosleep () in DLL
[Thread 7860] dosleep () in DLL
[Thread 736] dll_thread_detach
[Thread 7860] dll_thread_detach
[Thread 4976] dll_process_detach // main thread
The above input results show that every thread calls the DLL function dosleep and ends immediately. At this time, dll_thread_detach is called normally. At this time, you only need to change the code a little and you will see completely different results.
DWORD winapi threadproc (lpvoid lpparam)
{
Calltestdll ();
Dosomethingelse (); // delay the end of the thread
Return 0;
}
Output result 2:
[Thread 7448] dll_process_attach // main thread
[Thread 7448] loadlibrary = 0x0b1cf9d4
[Thread 7448] dosleep () in DLL
[Thread 6872] dll_thread_attach
[Thread 6556] dll_thread_attach
[Thread 6556] dosleep () in DLL
[Thread 6872] dosleep () in DLL
[Thread 7448] dll_process_detach // main thread
We found that the thread generated by createthread does not call dll_thread_detach.
Conclusion:
If the thread ends before the DLL is detached (call freelibrary), dll_thread_detach is called. If the thread ends after the DLL is uninstalled, dll_thread_detach will not be called.