10.2.4 The LibMain Function

Every dynamic link library must have a LibMain function. This function is called by the system whenever a process attaches to the DLL. For a process that explicitly calls the functions in a DLL (load-time dynamic linking), the LibMain function is called during the initialization phase of the process. For a process that uses LoadLibrary to attach to a DLL (run-time dynamic linking), LibMain is called before LoadLibrary returns.

Once a process has successfully attached to the DLL, its LibMain function is also called whenever a thread of that process starts up or terminates. These calls occur even if the thread causing the call does not use any functions in the DLL. Note that a thread detach event occurs when a thread terminates even if there was no corresponding thread attach event for that thread (which can happen if run-time linking occurs after the process has been running for a while). Also note that a thread attach LibMain call is not made for the thread that was the current thread when the process attach call was made. For example, if LibMain is called for process attachment when a process starts up, it will not be called again to attach the main thread.

Finally, the LibMain function is called when the process detaches from the DLL. Detaching from a DLL occurs automatically at process termination. Processes that used LoadLibrary to attach to a DLL can call FreeLibrary to detach. If a process terminates abnormally, as from a call to TerminateProcess or from a Ctrl-c interrupt, the LibMain functions of attached DLLs will not be called. LibMain should not be called explicitly by the code of your application.

LibMain does not have to do anything, but it can be useful for performing per process and per thread initialization and cleanup. The function returns an INT, where a value of 1 indicates success. If LibMain fails when it is called during process startup, a hard error occurs and the process is killed. If it fails when a process is attaching at run-time, the LoadLibrary call returns NULL to indicate failure. If LibMain fails at any of the other times that it is called (thread startup, thread termination, or process termination) the results are not fatal to the process. There is no way for a process to determine the value returned by a DLL's LibMain function. This means that a return value indicating failure is useful only when LibMain is called for process attachment. If failure occurs at any of the other times LibMain is called, the DLL must rely on its own global or static variables to record the failure. For example, suppose LibMain is used to allocate memory when each thread attaches to the DLL. An allocation failure cannot be reported by the LibMain return value. Instead, the DLL can only store the NULL result and report the failure when the process calls a DLL function that needs to use the memory.

The code fragment below illustrates a LibMain function that uses file mapping to map a block of shared memory into the address space of the process. When the ul_reason_being_called parameter indicates that a process is attaching to the DLL, CreateFileMapping is called to create a file mapping object that is backed by a portion of the system's paging file. This call can be used to create or to open a file mapping object. The first process to attach will create the file mapping, and subsequent processes will open a handle to it. Either way the function returns a handle to the file mapping object, and the GetLastError function is used to determine if the shared memory needs to be initialized.

Note that lpvSharedMem and hFileMappingObject are declared as global variables. The example assumes that the DLL's global data is not shared, so each attaching process will have its own instances of the variables. By default DLL data is not shared, but this could be modified by the DATA or SECTIONS statements in the DLL's module definition file. In this example, it is important that each process have its own instance of lpvSharedMem since the shared memory block is not necessarily mapped to the same address for each process.

#define SHMEMSIZE 4096

LPVOID lpvSharedMem = NULL;

HANDLE hFileMappingObject = NULL;

INT APIENTRY LibMain(HANDLE hInst,

ULONG ul_reason_being_called,

LPVOID lpReserved) {

BOOL fInitSharedMem, fIgnore;

switch (ul_reason_being_called) {

case DLL_PROCESS_ATTACH:

hFileMappingObject = CreateFileMapping((HANDLE) 0xffffffff,

NULL, PAGE_READWRITE, 0, SHMEMSIZE, "dllmemfilemap");

if (hFileMappingObject == NULL) return 0;

fInitSharedMem = (GetLastError() != ERROR_ALREADY_EXISTS);

lpvSharedMem = MapViewOfFile(hFileMappingObject,

FILE_MAP_WRITE, 0,0,0);

if (lpvSharedMem == NULL) return 0;

if (fInitSharedMem) memset(lpvSharedMem, '\0', SHMEMSIZE);

break;

case DLL_THREAD_ATTACH: break;

case DLL_THREAD_DETACH: break;

case DLL_PROCESS_DETACH:

if (lpvSharedMem != NULL)

fIgnore = UnmapViewOfFile(lpvSharedMem);

if (hFileMappingObject != NULL)

fIgnore = CloseHandle(hFileMappingObject);

break;

default: break;

}

return 1;

UNREFERENCED_PARAMETER(hInst);

UNREFERENCED_PARAMETER(lpReserved);

}