The most obvious difference between STRLIB.C and our Windows programs is the absence of WinMain. Instead, there is a function called LibMain. LibMain is called from the LIBENTRY.OBJ module. This is required because of the different ways in which Windows programs and Windows dynamic link libraries are initialized during startup.
When you link a Windows program with LINK, a function called __astart is linked into the program. This is the entry point to the program. On entry to __astart, the CPU registers contain the following information:
BX | Stack size |
CX | Heap size |
DI | Instance handle |
SI | Previous instance |
ES | Program segment prefix |
The start-up code (which comes from SLIBCEW.LIB) performs some initialization and then calls the WinMain function, which is the perceived entry point when you program for Windows in C.
For dynamic link libraries, no start-up code is provided in SDLLCEW.LIB. That's why LIBENTRY.OBJ (or something similar) is required. Windows calls LibEntry once (when the first program that requires the dynamic link library is loaded) with the CPU registers set as follows:
DI | Instance handle |
DS | Library's data segment |
CX | Heap size |
ES:SI | Command line |
Note the differences between these registers and those for a Windows program. A register containing the stack size isn't required, because library modules don't have a stack. A register containing the previous instance handle isn't required, because library modules can't have multiple instances. For most uses of libraries, the command-line parameter in ES:SI isn't used. LibEntry must return nonzero if initialization is successful and 0 if errors are encountered. A failed initialization causes Windows to not run the program that requires the library.
LIBENTRY initializes the local heap by calling LocalInit and then calls LibMain, which is in STRLIB.C. The LibMain definition looks like this:
int FAR PASCAL LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize,
LPSTR lpszCmdLine)
{
if (wHeapSize > 0)
UnlockData (0) ;
return 1 ;
}
This simply unlocks the data segment of the library (which is locked by the LocalInit call in LIBENTRY) and returns 1. If you need to do additional initialization when the library is first loaded, you can do it here.
Note the differences implied here between programs and libraries. On entry to a program, the start-up code passes control to WinMain, which performs initialization and then enters a message loop. Multitasking takes place during GetMessage calls. The program exits the message loop (and WinMain) only when the program retrieves a WM_QUIT message from the message queue. On entry to a library, the start-up code must perform initialization and then return control to Windows with a nonzero value. The rest of the library sits dormant in memory until another module calls one of the exported functions.
You can also add a ”de-initialization“ routine to a library; the routine is called when a program using the library terminates. Information on this can be found in Chapter 20 of the Guide to Programming book, included in the Windows Software Development Kit.