Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tato část ukazuje použití funkce vstupního bodu knihovny DLL k nastavení indexu místního úložiště vlákna (TLS) pro poskytnutí privátního úložiště pro každé vlákno vícevláknového procesu.
Index TLS je uložený v globální proměnné a zpřístupňuje ho všem funkcím knihovny DLL. Tento příklad předpokládá, že globální data knihovny DLL nejsou sdílena, protože index TLS není nutně stejný pro každý proces, který načte knihovnu DLL.
Funkce vstupního bodu používá funkci TlsAlloc k přidělení indexu TLS při každém načtení knihovny DLL. Každé vlákno pak může tento index použít k uložení ukazatele na vlastní blok paměti.
Když je funkce vstupního bodu volána s hodnotou DLL_PROCESS_ATTACH, kód provede následující akce:
- Používá funkci TlsAlloc k přidělení indexu TLS.
- Přidělí blok paměti, který bude použit výhradně počátečním vláknem procesu.
- Používá index TLS ve volání funkce TlsSetValue k uložení adresy bloku paměti do slotu TLS přidruženého k indexu.
Pokaždé, když proces vytvoří nové vlákno, volá se funkce vstupního bodu s hodnotou DLL_THREAD_ATTACH. Funkce vstupního bodu pak přidělí blok paměti pro nové vlákno a uloží na něj ukazatel pomocí indexu TLS.
Pokud funkce vyžaduje přístup k datům přidruženým k indexu TLS, zadejte index ve volání funkce TlsGetValue. Tím se načte obsah slotu TLS pro volající vlákno, což je v tomto případě ukazatel na blok paměti pro data. Pokud proces používá propojení s touto knihovnou DLL za běhu načítání, je funkce vstupního bodu dostatečná ke správě místního úložiště vlákna. K problémům může dojít u procesu, který používá propojení za běhu, protože funkce vstupního bodu není volána pro vlákna, která existují předtím, než je volána funkce LoadLibrary, takže paměť TLS není pro tato vlákna přidělena. Tento příklad řeší tento problém kontrolou hodnoty vrácené funkcí TlsGetValue a přidělením paměti, pokud hodnota indikuje, že slot TLS pro toto vlákno není nastaven.
Pokud už každé vlákno nepotřebuje používat index TLS, musí uvolnit paměť, jejíž ukazatel je uložen v slotu TLS. Po dokončení všech vláken pomocí indexu TLS použijte funkci TlsFree k uvolnění indexu.
Když se vlákno ukončí, je volána funkce vstupního bodu s hodnotou DLL_THREAD_DETACH a paměť pro toto vlákno je uvolněna. Když se proces ukončí, volá se funkce vstupního bodu s hodnotou DLL_PROCESS_DETACH a paměť odkazovaná ukazatelem v indexu TLS se uvolní.
// The DLL code
#include <windows.h>
static DWORD dwTlsIndex; // address of shared memory
// DllMain() is the entry-point function for this DLL.
BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
DWORD fdwReason, // reason called
LPVOID lpvReserved) // reserved
{
LPVOID lpvData;
BOOL fIgnore;
switch (fdwReason)
{
// The DLL is loading due to process
// initialization or a call to LoadLibrary.
case DLL_PROCESS_ATTACH:
// Allocate a TLS index.
if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
return FALSE;
// No break: Initialize the index for first thread.
// The attached process creates a new thread.
case DLL_THREAD_ATTACH:
// Initialize the TLS index for this thread.
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData != NULL)
fIgnore = TlsSetValue(dwTlsIndex, lpvData);
break;
// The thread of the attached process terminates.
case DLL_THREAD_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
break;
// DLL unload due to process termination or FreeLibrary.
case DLL_PROCESS_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
// Release the TLS index.
TlsFree(dwTlsIndex);
break;
default:
break;
}
return TRUE;
UNREFERENCED_PARAMETER(hinstDLL);
UNREFERENCED_PARAMETER(lpvReserved);
}
// The export mechanism used here is the __declspec(export)
// method supported by Microsoft Visual Studio, but any
// other export method supported by your development
// environment may be substituted.
#ifdef __cplusplus // If used by C++ code,
extern "C" { // we need to export the C interface
#endif
__declspec(dllexport)
BOOL WINAPI StoreData(DWORD dw)
{
LPVOID lpvData;
DWORD * pData; // The stored memory pointer
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData == NULL)
{
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData == NULL)
return FALSE;
if (!TlsSetValue(dwTlsIndex, lpvData))
return FALSE;
}
pData = (DWORD *) lpvData; // Cast to my data type.
// In this example, it is only a pointer to a DWORD
// but it can be a structure pointer to contain more complicated data.
(*pData) = dw;
return TRUE;
}
__declspec(dllexport)
BOOL WINAPI GetData(DWORD *pdw)
{
LPVOID lpvData;
DWORD * pData; // The stored memory pointer
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData == NULL)
return FALSE;
pData = (DWORD *) lpvData;
(*pdw) = (*pData);
return TRUE;
}
#ifdef __cplusplus
}
#endif
Následující kód ukazuje použití funkcí DLL definovaných v předchozím příkladu.
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
#define DLL_NAME TEXT("testdll")
VOID ErrorExit(LPSTR);
extern "C" BOOL WINAPI StoreData(DWORD dw);
extern "C" BOOL WINAPI GetData(DWORD *pdw);
DWORD WINAPI ThreadFunc(VOID)
{
int i;
if(!StoreData(GetCurrentThreadId()))
ErrorExit("StoreData error");
for(i=0; i<THREADCOUNT; i++)
{
DWORD dwOut;
if(!GetData(&dwOut))
ErrorExit("GetData error");
if( dwOut != GetCurrentThreadId())
printf("thread %d: data is incorrect (%d)\n", GetCurrentThreadId(), dwOut);
else printf("thread %d: data is correct\n", GetCurrentThreadId());
Sleep(0);
}
return 0;
}
int main(VOID)
{
DWORD IDThread;
HANDLE hThread[THREADCOUNT];
int i;
HMODULE hm;
// Load the DLL
hm = LoadLibrary(DLL_NAME);
if(!hm)
{
ErrorExit("DLL failed to load");
}
// Create multiple threads.
for (i = 0; i < THREADCOUNT; i++)
{
hThread[i] = CreateThread(NULL, // default security attributes
0, // use default stack size
(LPTHREAD_START_ROUTINE) ThreadFunc, // thread function
NULL, // no thread function argument
0, // use default creation flags
&IDThread); // returns thread identifier
// Check the return value for success.
if (hThread[i] == NULL)
ErrorExit("CreateThread error\n");
}
WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE);
FreeLibrary(hm);
return 0;
}
VOID ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
ExitProcess(0);
}
Související témata