LIBMAIN function

Purpose | LIBMAIN (or its synonym DLLMAIN) is an optional user-defined function called by Windows each time a DLL is loaded into, and unloaded from, memory. The PBLIBMAIN function performs a similar task to LIBMAIN, but takes no parameters.

Syntax

FUNCTION { LIBMAIN | DLLMAIN } ( _

BYVAL hInstance AS DWORD, _

BYVAL lReason AS LONG, _

BYVAL lReserved AS LONG ) AS LONG

In 32-bit Windows, LIBMAIN is called by Windows each time a DLL is loaded or unloaded by an application or process, and (usually) when a thread is started and stopped. Your code should never call LIBMAIN.

Remarks | The LIBMAIN / DLLMAIN function provides the following parameters:
hInstance | The unique instance handle of the DLL. This handle is used by the calling application to identify the DLL. The instance handle value is commonly used to load resources embedded within the DLL, and to obtain the actual file name of the DLL (via the GetModuleFilename API function). In these cases, it is common to copy the hInstance value to a global variable, allowing the instance handle value to be utilized elsewhere in the DLL.

lReason

This flag indicates why the DLL entry-point is being called. It can be one of the following values (as defined in WIN32API.INC):

%DLL_PROCESS_ATTACH | Indicates that the DLL is being loaded by a process (another DLL or EXE is loading the DLL). DLLs can use this opportunity to initialize any instance or global data, such as arrays. lReserved is zero if the DLL is being loaded explicitly (run-time linking) using LoadLibrary(), or non-zero if the DLL is being loaded implicitly (load-time linking) during process initialization.
%DLL_PROCESS_DETACH | Indicates that the DLL is being cleanly unloaded or detached from the calling application. DLLs can take this opportunity to clean up all resources for all threads attached and known to the DLL. This is functionally equivalent to the WEP function in 16-bit DLLs. lReserved is zero if LIBMAIN was executed via the FreeLibrary API and the DLLs reference count reached zero (no further instances of the DLL are loaded), or non-zero if LIBMAIN is executed during process termination. A %DLL_PROCESS_DETACH does not generate %DLL_THREAD_DETACH for active threads.
%DLL_THREAD_ATTACH | Indicates that the DLL is being loaded by a new thread in the calling application. DLLs can use this opportunity to initialize any Thread Local Storage (TLS). This execution occurs in the context of the new thread.
%DLL_THREAD_DETACH | Indicates that the thread is exiting cleanly. If the DLL has allocated any thread-specific storage (Thread Local Storage or TLS), it should be released. This may occur even if there was no matching %DLL_THREAD_ATTACH call. A %DLL_PROCESS_DETACH does not generate %DLL_THREAD_DETACH for active threads.

lReserved | The lReserved parameter specifies further aspects of the DLL initialization and cleanup. If lReason is %DLL_PROCESS_ATTACH, lReserved is zero (0) for explicit (dynamic) loads and non-zero for implicit loads. If lReason is %DLL_PROCESS_DETACH, lReserved is zero if LIBMAIN has been called by using the FreeLibrary API call, and non-zero if LIBMAIN has been called during process termination.
Return value | If LIBMAIN is called with %DLL_PROCESS_ATTACH, your LIBMAIN function should return a zero (0) if any part of your initialization process fails, or a one (1) if no errors were encountered. If a zero is returned, Windows will abort and unload the DLL from memory. When LIBMAIN is called with any other value than %DLL_PROCESS_ATTACH, the return value is ignored.

Restrictions

Note that Windows does not guarantee that LIBMAIN will be called in a "balanced" manner. For example, a %DLL_PROCESS_ATTACH is not followed by a %DLL_THREAD_ATTACH for the primary thread. In some conditions, %DLL_THREAD_DETACH may not occur at all. Further discussion on these Windows traits are beyond the scope of this documentation; however, an excellent source of information can be found in "Win32 Programming", Rector/Newcomer, ISBN 0-201-63492-9.

At the point where a DLL is loaded into memory during process startup, Windows only guarantees that the KERNEL32.DLL system library will be loaded in memory. On this basis, API calls made from within LIBMAIN must be restricted to the range of API functions present in KERNEL32.DLL, with the exception of the LoadLibrary, LoadLibraryEx, and FreeLibrary API functions.

In addition, code within LIBMAIN must not call API functions in any other DLL (for example, USER32.DLL, SHELL32.DLL, ADVAPU32.DLL, GDI32.DLL, etc), because some API functions in those DLLs may attempt to load other libraries via LoadLibrary, etc. For example, never call the MessageBox API function from within LIBMAIN, nor use the related MSGBOX function or MSGBOX statement.

Failure to observe these restrictions will result in Access Violation or General Protection Faults (GPFs), typically caused by the execution of code in DLLs that has yet to be initialized.

See also | DLLMAIN, PBLIBMAIN, PBMAIN, THREAD CREATE, WINMAIN

Example

#DIM ALL

#COMPILE DLL "LIBTEST.DLL"

#INCLUDE "WIN32API.INC"

GLOBAL gNumOfTimes AS DWORD

FUNCTION LIBMAIN(BYVAL hInstance AS DWORD, _

BYVAL lReason AS LONG, _

BYVAL lReserved AS LONG) AS LONG

INCR gNumOfTimes

SELECT CASE AS LONG lReason

CASE %DLL_PROCESS_ATTACH

' This DLL has been mapped into the memory context of

' the calling program, and can be initialized as required.

' Here we return a non-zero LIBMAIN result to indicate success.

LIBMAIN = 1

EXIT FUNCTION

CASE %DLL_PROCESS_DETACH

' This DLL is about to be unloaded

EXIT FUNCTION

CASE %DLL_THREAD_ATTACH

' A [New] thread is starting (see THREADID)

EXIT FUNCTION

CASE %DLL_THREAD_DETACH

' This thread is closing (see THREADID)

EXIT FUNCTION

END SELECT

' Theoretically execution should never get to this point.

' However, if the DLL is being implicitly linked then return

' Zero (0) and the process (program) will fail to start

' running. For Explicit linking, returning Zero (0) will

' simply cause the LoadLibrary/LoadLibraryEx API call to fail.

LIBMAIN = 0 ' Indicate failure to initialize the DLL!

END FUNCTION

SUB TestIt ALIAS "TestIt" () EXPORT

MSGBOX "TestIt" + $CRLF + _"gNumOfTimes =" + STR$(gNumOfTimes)

END SUB