Creating the C-Language Source File

This section provides C source code as a template for creating a DLL. Like any other type of C program, DLLs can contain multiple functions. Each function that other applications or DLLs will use must be declared as FAR, and must be listed in the EXPORTS section of the library's module-definition (.DEF) file. For more information on how to create the module-definition file for this sample library, see topic .

/* MINDLL.C — Sample DLL code to demonstrate minimum code needed to create a dynamic-link library. */

#include WINDOWS.H

int FAR PASCAL LibMain (HANDLE hInstance,

WORD wDataSeg,

WORD cbHeapSize,

LPSTR lpszCmdLine)

{

.

.

/* Perform DLL initialization. */

.

.

if (cbHeapSize != 0) /* If DLL data seg is MOVABLE */

UnlockData(0);

return (1); /* Initialization successful. */

}

VOID FAR PASCAL MinRoutine (int iParam1, LPSTR lpszParam2)

{

char cLocalVariable; /* Local variables on stack. */

.

.

/* MinRoutine Code goes here. */

.

.

}

VOID FAR PASCAL WEP (int nParameter)

{

if (nParameter == WEP_SYSTEMEXIT)

{

/* System shutdown in progress. Respond accordingly. */

return (1);

}

else

{

if (nParameter == WEP_FREE_DLL)

{ /* DLL use count is zero. Every application that had

loaded the DLL has freed it. */

return (1);

}

else

{

/* Undefined value. Ignore. */

return (1);

}

}

}

DLL source code uses WINDOWS.H in the same way as does application source code. WINDOWS.H contains data-type definitions, API entry-point definitions, and other useful parameter information.

The PASCAL declaration defines the parameter-passing and stack-cleanup convention for this routine. This is not required for DLL routines, but its use results in slightly smaller and faster code, and therefore its use is strongly recommended. The Pascal calling convention cannot be used for routines with a variable number of parameters, or for calling C run-time routines. In such cases, the CDECL calling convention is required.

There are two parameters shown on the MinRoutine parameter list, but DLL routines can have as few or as many parameters as are required. The only requirement is that pointers passed from outside the DLL module must be long pointers.

Initializing a DLL

You must include an automatic initialization function in a DLL. The initialization function performs one-time start-up processing. Windows calls the initialization function once, when the library is initially loaded. When subsequent applications that use the library load the library, Windows does not call the initialization function. Instead, Windows simply increments the DLL's use count.

Windows maintains a library in memory as long as its use count is greater than zero. If a library's use count becomes zero, it is removed from memory. When an application causes the library to be reloaded into memory, the initialization function will again be called.

Following are some typical tasks a DLL's initialization function might perform:

Registering window classes for window procedures contained in the DLL

Initializing the DLL's local heap

Setting initial values for the DLL's global variables

The library initialization procedure is required in order to allocate the local heap of your DLL. The local heap must be created before the DLL calls any local heap functions, such as LocalAlloc. While Windows automatically initializes the local heap for Windows applications, DLLs must explicitly initialize the local heap by calling the LocalInit function.

In addition, you should include the following declaration in the initialization
procedure:

extrn __acrtused:abs

This ensures that the DLL will be linked with the DLL start-up code in the Windows DLL C run-time libraries (xDLLCyW.LIB) if the DLL does not call any C run-time routines.

Initialization information is passed in hardware registers to a library when it is loaded. Since hardware registers are not accessible from the C language, you must provide an assembly language routine to obtain these values. The location and value of the heap information are as follows:

Register Value

DI The DLL's instance handle
DS The DLL's data segment, if any
CX The heap size specified in the DLL's .DEF file
ES:SI The command line (in the lpCmdLine field of the LoadModule function's lpParameterBlock parameter)

The following is a sample LibMain function:

int FAR PASCAL LibMain (HANDLE hInstance,

WORD wDataSeg,

WORD cbHeapSize,

LPSTR lpszCmdLine)

{

.

.

/* Perform DLL initialization. */

.

.

if (cbHeapSize != 0) /* If DLL data seg is MOVABLE */

UnlockData(0);

return (1); /* Successful installation. Otherwise, return(0); */

}

LibMain takes four parameters: hInstance, wDataSeg, cbHeapSize, and lpszCmdLine. The first parameter, hInstance, is the instance handle of the DLL. The wDataSeg parameter is the value of the data-segment (DS) register. The cbHeapSize parameter is the size of the heap defined in the module-definition file. The lpszCmdLine parameter contains command line information and is rarely used by DLLs.

If you do not want the DLL data segment to be locked, the call to UnlockData
is necessary because the LocalInit function leaves the data segment locked.
UnlockData restores the data segment to its normal unlocked state.

If the DLL initialization has been successful, the DLL should return a value of 1. A value of zero indicates that initialization was not successful, and the DLL is unloaded from system memory.

Terminating a DLL

Windows DLLs must include a termination function. A termination function, sometimes called an exit procedure, performs cleanup for a DLL before it is unloaded.

DLLs that contain window procedures that have been registered (using RegisterClass) are not required to remove the class registration (using UnRegisterClass); Windows does this automatically when the DLL terminates.

A sample termination function is shown next. The termination function
should be defined as shown here. A single argument is passed, nParameter, which indicates whether all of Windows is shutting down
(nParameter==WEP_SYSTEMEXIT), or just the single DLL (WEP_FREE_DLL). It always returns 1 to indicate success.

VOID FAR PASCAL WEP (int nParameter)

{

if (nParameter == WEP_SYSTEMEXIT)

{

/* System shutdown in progress. Respond accordingly.*/

return (1);

}

else

{

if (nParameter == WEP_FREE_DLL)

{ /* DLL use count is zero. Every application that had

loaded the DLL has freed it. */

return (1);

}

else

{ /* Undefined value. Ignore. */

return (1);

}

}

}

The name of the termination function must be WEP, and it must be included in the EXPORTS section of the DLL's module-definition file. It is strongly recommended, for performance reasons, that the ordinal entry value and the RESIDENTNAME keyword be used, to minimize the time used to find this function. Since the use of the RESIDENTNAME keyword causes the export information for this routine to stay in memory at all times, it is not recommended for use with other exported functions.

See the following section for more information.