Title:  Win32 SDK October Release:  The C Run-time and DLLs

Summary:

This document contains the following sections:

   Section 1:  Three Forms of C Run-time Library Are Available
   Section 2:  Using the C Run-time Libraries in a DLL
   Section 3:  Using NTWIN32.MAK To Simplify The Build Process
   Section 4:  Problems with CRTDLL.LIB and the SDK DLL samples
   Section 5:  Problems Encountered When Using Multiple C Run-time Libraries
   Section 6:  Problems Encountered When Improperly Mixing Library Types
   Section 7:  Work-Around For Bug In LIBCMT.LIB

More Information:

Section 1:  Three Forms of C Run-time Libraries Are Available

There are three forms of the C Run-time library provided with the Win32 SDK:

   - LIBC.LIB is a statically linked library for single-threaded programs.

   - LIBCMT.LIB is a statically linked library which supports multi-threaded
     programs.

   - CRTDLL.LIB is an import library for CRTDLL.DLL, which also supports 
     multi-threaded programs.

Section 2:  Using the C Run-time Libraries in a DLL

When linking a DLL with any of the C Run-time libraries, either the entry 
point for the DLL must be _CRT_INIT() or the user DLL's initialization code
must explicitly call _CRT_INIT() every time the DLL entry point is called. 
This permits the C Run-time libraries to properly allocate and initialize 
C Run-time data when a process or thread is attaching to the DLL, to 
properly clean up C Run-time data when a process is detaching from the DLL, 
and for global C++ objects in the DLL to be properly constructed and 
destructed.

1)  If you do not have your own DLL entry point:

    Specify _CRT_INIT() as the entry point of the DLL. Assuming that you've
    included ntwin32.mak, which defines DLLENTRY as '@12', add the following
    option to the DLL's link line:

       -entry:_CRT_INIT$(DLLENTRY)

2)  If you *do* have your own DLL entry point, do the following in the entry
    point:

    a) Use this prototype for _CRT_INIT():

       BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, 
          LPVOID lpReserved);

       For information on _CRT_INIT() return values, see the documentation for 
       DllEntryPoint; it returns the same values that _CRT_INIT() does.

    b) On PROCESS_ATTACH and THREAD_ATTACH (See "DllEntryPoint" in the Win32
       API Reference for more info on these flags), call _CRT_INIT() at the 
       beginning of the initialization function, before any C Run-time 
       functions are called or any floating-point operations are performed.

    c) Call your own process/thread initialization/termination code

    d) On PROCESS_DETACH and THREAD_DETACH, call _CRT_INIT() near the end of 
       the initialization routine, after all C Run-time functions are called 
       and all floating-point operations are completed.

Be sure to pass on to _CRT_INIT() all of the parameters of the entry point; 
_CRT_INIT() expects those parameters otherwise, things will not work 
reliably (in particular, fdwReason is required to determine whether process 
initialization or termination is needed).

Below is a skeleton sample entry point function that shows when and how to 
make these calls to _CRT_INIT() in the DLL entry point:

    BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, 
       LPVOID lpReserved)
    {

    /* Initialize the C Run-Time *before* calling any of your code */
    
    if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
        if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
            return(FALSE);

    /* Place your DLL init/termination code here.  If you are converting */
    /* and have an existing LibMain(), you can call it here, like this   */
    /*                                                                   */
    /*  if (fdwReason == DLL_PROCESS_ATTACH)                             */
    /*      LibMain();                                                   */

    /* Terminate the C Run-Time *after* all your code */
    
    if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
        if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
            return(FALSE);
    return(TRUE);
    }

Section 3: Using NTWIN32.MAK To Simplify The Build Process

There are macros defined in NTWIN32.MAK that can be used to simplify your
makefiles and to ensure that they are properly built to avoid conflicts.
For this reason, Microsoft highly recommends using NTWIN32.MAK and the 
macros therein.

For compilation, use one of the following
    $(cvarsdll)          for single-threaded apps/DLLs using CRTDLL.LIB 
    $(cvarsmtdll)        for multi-threaded apps/DLLs using CRTDLL.LIB
  
For linking, use one of the following
    $(conlibsdll)        for Console DLLs using CRTDLL.LIB
    $(guilibsdll)        for GUI DLLs using CRTDLL.LIB

Section 4: Problems with CRTDLL.LIB and the SDK DLL samples

The October release of the C Run-time library CRTDLL.LIB is missing the
code that defines _CRT_INIT(). As discussed above, it is vital to call
this function when linking a DLL with CRTDLL.LIB. An object file
(crtdll.obj) has been supplied on CompuServe in the MSWIN32 forum in 
library 7, in the archive CRTDLL.ZIP, which fixes this problem. Be sure 
to link in this object file when linking with crtdll.lib, or else the 
linker will report that '_CRT_INIT@12' is an undefined external. 

In detail, here what to do once you have downloaded this file, assuming 
once again that you are including ntwin32.mak in the makefile:

    1) Copy crtdll.obj into one of the directories defined in the LIB 
       environment variable (usually \mstools\lib).

    2) Add the following lines *after* the "!include <ntwin32.mak>" line:

       conlibsdll=crtdll.obj $(conlibsdll)
       guilibsdll=crtdll.obj $(conlibsdll)

    3) Add the "-entry:_CRT_INIT$(DLLENTRY)" option to the DLL link command
       or call _CRT_INIT() in the entry point, as desired.

Several of the sample DLLs included with the October Win32 SDK do not call 
_CRT_INIT() or supply it as the entry point. These examples happen to work
because they do not call any of the C Run-time functions that require this 
initialization. However, if you modify one of the sample DLLs and add any 
C Run-time calls, these calls may fail. In order to avoid this problem, be 
sure to modify the sample following the steps given above and in Section 2. 
Also, both the EXE and DLL should be linked with $(guilibsdll), not 
$(guilibs) or $(guilibsmt) (see Section 6 below).

Note that CRTDLL.OBJ will be included properly in the next release, so it 
will not be necessary to explicitly link it in with future versions of the 
Win32 SDK. Also, the problems in the samples will be fixed in the next 
release of the Win32 SDK.

Section 5:  Problems Encountered When Using Multiple C Run-time Libraries

If an application that makes C Run-time calls links to a DLL that also 
makes C Run-time calls, be aware that if they are both linked with one of
the statically-linked C Run-time libraries (LIBC.LIB or LIBCMT.LIB), the 
EXE and DLL will have separate copies of all C Run-time functions and 
global variables. This means that C Run-time data can not be shared 
between the EXE and the DLL. Some of the problems that can occur as a
result are; 

    - Passing buffered stream handles from the EXE/DLL to the other
      module
    - Allocating memory with a C Run-time call in the EXE/DLL and
      reallocating or freeing it in the other module
    - Checking or setting the value of the global errno variable in the
      EXE/DLL and expecting it to be the same in the other module. A
      related problem is calling perror() in the opposite module from
      where the C Run-time error occurred, since perror() uses errno.

To avoid these problems, link both the EXE and DLL with CRTDLL.LIB, which 
allows both the EXE and DLL to use the common set of functions and data 
contained within CRTDLL.DLL, and C Run-time data such as stream handles 
can then be shared by both the EXE and DLL.

Section 6: Problems Encountered When Improperly Mixing Library Types

One complication that may come about when linking a DLL with
CRTDLL.LIB is that any EXEs that link to that DLL *must* also be linked
with CRTDLL.LIB. Linking the EXE with either LIBC.LIB or LIBCMT.LIB 
and the DLL with CRTDLL.LIB is a non-supported configuration and might
cause problems. Unless you are sure that the DLL will only be called by
EXEs that are linked with CRTDLL.LIB, you must link the DLL with one of
the statically linked C Run-time libriaries: LIBC.LIB or LIBCMT.LIB. If
you are sure that this will never occur, it is best if you link the EXE
and all DLLs it will call with CRTDLL.LIB.

Concerning linking DLLs with LIBC.LIB, be aware that if there is a
possibility that such a DLL will be called by a multi-threaded program, 
the DLL will not support multiple threads running in the DLL at the same
time, which can cause major problems. If there is a possibility that the
DLL will be called by multi-threaded programs, be sure to link it with one
of the libraries that support multi-threaded programs (LIBCMT.LIB or 
CRTDLL.LIB).

Section 7:  Work-Around For Bug In LIBCMT.LIB

Please be aware that there is a bug in LIBCMT.LIB concerning 
initalization of the C Run-time heap; if using LIBCMT.LIB for a DLL, then
you must explicity call the _heap_init() function in the DLL entry point
on PROCESS_ATTACH. This work-around is only needed for the October 
release; this problem will be fixed in the next release and this call 
should then be removed in builds for later releases.

The following is the skeleton sample from above modified to show how to
call _heap_init() to work around this problem. Use this as the correct 
model for your DLL entry points when using LIBCMT.LIB:

    BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason,
        LPVOID lpReserved)
    {
    #ifdef This_fix_is_for_the_Oct_Beta_only!

    void __cdecl _heap_init(void);  /* only needed for work-around  */

    /* There is a bug in the October Beta version of _CRT_INIT() in */
    /* LIBCMT.LIB - this is not needed for later C Run-time library */
    /* releases.                                                    */

    if (fdwReason == DLL_PROCESS_ATTACH)
        _heap_init();          /* work around for bug in LIBCMT.LIB */

    #endif

    /* Initialize the C Run-Time *before* calling any of your code */
    
    if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
        if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
            return(FALSE);

    /* Place your DLL init/termination code here.  If you are converting */
    /* and have an existing LibMain(), you can call it here, like this   */
    /*                                                                   */
    /*  if (fdwReason == DLL_PROCESS_ATTACH)                             */
    /*      LibMain();                                                   */

    /* Terminate the C Run-Time *after* all your code */
    
    if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
        if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
            return(FALSE);
    return(TRUE);
    }
