|< Windows Processes & Threads: Synchronization 5 | Main | Dynamic Link Library, DLL 2 >| Site Index | Download |


 

 

 

MODULE BB

DYNAMIC LINK LIBRARY - DLL

Part 1: STORY

 

 

 

 

What do we have in this Module?

  1. Dynamic-Link Library and C Run-Time Story

  2. Differences Between Applications and DLLs

  3. Advantages of Using DLLs

  4. Type of DLLs

  5. Linking an Executable to a DLL

  6. Using Linking Implicitly

  7. Using Linking Explicitly

  8. Determining Which Linking Method to Use

  9. Implicit Linking

  10. Explicit Linking

  11. Creating a Resource-Only DLL

  12. Importing and Exporting

  13. Using a .DEF file

  14. Using __declspec

  15. Importing into an Application Using __declspec(dllimport)

  16. Exporting from a DLL

  17. Exporting from a DLL Using __declspec(dllexport)

  18. Initializing a DLL

  19. Initializing Extension DLLs (For MFC programs)

  20. Initializing Non-MFC DLLs

  21. Run-Time Library Behavior

 

 

 

 

 

 

 

 

 

 

 

 

 

 

My Training Period: xx hours. Before you begin, read some instruction here. The Windows MFC programming (GUI programming) for DLL can be found at MFC GUI Programming Step-by-step Tutorial.

 

Abilities that supposed to be acquired:

Dynamic-Link Library and C Run-Time Story

 

Note: This Module is a general MSDN documentation that covers the C/C++ Run-Time Library and MFC on Visual C++. Let get the big picture first!

 

A dynamic-link library (DLL) is an executable file that acts as a shared library of functions. Dynamic linking provides a way for a process to call a function that is not part of its executable code. The executable code for the function is located in a DLL, which contains one or more functions that are compiled, linked, and stored separately from the processes that use them. DLLs also facilitate the sharing of data and resources. Multiple applications can simultaneously access the contents of a single copy of a DLL in memory. Dynamic linking differs from static linking in that it allows an executable module (either a .dll or .exe file) to include only the information needed at run time to locate the executable code for a DLL function. In static linking, the linker gets all of the referenced functions from the static link library and places it with your code into your executable. Using dynamic linking instead of static linking offers several advantages. DLLs save memory, reduce swapping, save disk space, upgrade easier, provide after-market support, provide a mechanism to extend the MFC library classes, support multi language programs, and ease the creation of international versions.

 

Differences Between Applications and DLLs

 

Even though DLLs and applications are both executable program modules, they differ in several ways. To the end-user, the most obvious difference is that DLLs are not programs that can be directly executed. From the system's point of view, there are two fundamental differences between applications and DLLs:

  1. An application can have multiple instances of itself running in the system simultaneously, whereas a DLL can have only one instance.

  2. An application can own things such as a stack, global memory, file handles, and a message queue, but a DLL cannot.

 

Advantages of Using DLLs

 

Dynamic linking has the following advantages:

  1. Saves memory and reduces swapping. Many processes can use a single DLL simultaneously, sharing a single copy of the DLL in memory. In contrast, Windows must load a copy of the library code into memory for each application that is built with a static link library.

  2. Saves disk space. Many applications can share a single copy of the DLL on disk. In contrast, each application built with a static link library has the library code linked into its executable image as a separate copy.

  3. Upgrades to the DLL are easier. When the functions in a DLL change, the applications that use them do not need to be recompiled or re-linked as long as the functions' arguments and return values do not change. In contrast, statically linked object code requires that the application be re-linked when the functions change.

  4. Provides after-market support. For example, a display driver DLL can be modified to support a display that was not available when the application was shipped.

  5. Supports multi language programs. Programs written in different programming languages can call the same DLL function as long as the programs follow the function's calling convention. The programs and the DLL function must be compatible in the following ways: the order in which the function expects its arguments to be pushed onto the stack, whether the function or the application is responsible for cleaning up the stack, and whether any arguments are passed in registers.

  6. Provides a mechanism to extend the MFC library classes. You can derive classes from the existing MFC classes and place them in an MFC extension DLL for use by MFC applications.

  7. Eases the creation of international versions. By placing resources in a DLL, it is much easier to create international versions of an application. You can place the strings for each language version of your application in a separate resource DLL, and have the different language versions load the appropriate resources.

 

A potential disadvantage to using DLLs is that the application is not self-contained; it depends on the existence of a separate DLL module.

 

 

Type of DLLs

 

Using Visual C++, you can build:

 

  1. Win32 DLLs in C or C++ that do not use the Microsoft Foundation Class Library (MFC).

  2. You can create a non-MFC DLL project with the Win32 Application Wizard.

  3. The MFC library itself is available, in either static link libraries or in a number of DLLs, with the MFC DLL Wizard. If your DLL is using MFC, Visual C++ supports three different DLL development scenarios:

Linking an Executable to a DLL

 

An executable file links to (or loads) a DLL in one of two ways:

  1. Implicit linking.

  2. Explicit linking.

 

Implicit linking is sometimes referred to as static load or load-time dynamic linking. Explicit linking is sometimes referred to as dynamic load or run-time dynamic linking. With implicit linking, the executable using the DLL links to an import library (.LIB file) provided by the maker of the DLL. The operating system loads the DLL when the executable using it is loaded. The client executable calls the DLL's exported functions just as if the functions were contained within the executable.

With explicit linking, the executable using the DLL must make function calls to explicitly load and unload the DLL, and to access the DLL's exported functions. The client executable must call the exported functions through a function pointer. An executable can use the same DLL with either linking method. Furthermore, these mechanisms are not mutually exclusive, as one executable can implicitly link to a DLL and another can attach to it explicitly.

 

Using Linking Implicitly

 

To implicitly link to a DLL, executables must obtain the following from the provider of the DLL:

  1. A header file (.H file) containing the declarations of the exported functions and/or C++ classes.

  2. An import library (.LIB files) to link with. The linker creates the import library when the DLL is built.

  3. The actual DLL (.DLL file).

 

Executables using the DLL must include the header file containing the exported functions (or C++ classes) in each source file that contains calls to the exported functions. From a coding perspective, the function calls to the exported functions are just like any other function call. To build the calling executable file, you must link with the import library. If you are using an external makefile, specify the file name of the import library where you list other object (.OBJ) files or libraries that you are linking with. The operating system must be able to locate the .DLL file when it loads the calling executable.

 

Using Linking Explicitly

 

With explicit linking, applications must make a function call to explicitly load the DLL at run time. To explicitly link to a DLL, an application must:

  1. Call LoadLibrary() (or a similar function) to load the DLL and obtain a module handle.

  2. Call GetProcAddress() to obtain a function pointer to each exported function that the application wants to call. Because applications are calling the DLL's functions through a pointer, the compiler does not generate external references, so there is no need to link with an import library.

  3. Call FreeLibrary() when done with the DLL.

 

For example:

typedef UINT (CALLBACK* LPFNDLLFUNC1)(DWORD, UINT);

...

 

HINSTANCE hDLL;               // Handle to DLL

LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer

DWORD dwParam1;

UINT  uParam2, uReturnVal;

 

hDLL = LoadLibrary("MyDLL");

if (hDLL != NULL)

{

   lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");

   if (!lpfnDllFunc1)

   {

      // handle the error

      FreeLibrary(hDLL);    

      return SOME_ERROR_CODE;

   }

   else

   {

      // call the function

      uReturnVal = lpfnDllFunc1(dwParam1, uParam2);

   }

}

Determining Which Linking Method to Use

 

Implicit Linking

 

Implicit linking occurs when an application's code calls an exported DLL function. When the source code for the calling executable is compiled or assembled, the DLL function call generates an external function reference in the object code. To resolve this external reference, the application must link with the import library (.LIB file) provided by the maker of the DLL. The import library only contains code to load the DLL and to implement calls to functions in the DLL. Finding an external function in an import library informs the linker that the code for that function is in a DLL. To resolve external references to DLLs, the linker simply adds information to the executable file that tells the system where to find the DLL code when the process starts up.

When the system starts a program that contains dynamically linked references, it uses the information in the program's executable file to locate the required DLLs. If it cannot locate the DLL, the system terminates the process and displays a dialog box that reports the error or if you are in the process of building an application, the following error message may be output:

testdll.obj : error LNK2019: unresolved external symbol "int __cdecl mydll(char *)" (?mydll@@YAHPAD@Z) referenced in function _main

Debug/mydlltest.exe : fatal error LNK1120: 1 unresolved externals

Otherwise, the system maps the DLL modules into the process's address space. If any of the DLLs has an entry-point function (for initialization and termination code), the operating system calls the function. One of the parameters passed to the entry-point function specifies a code that indicates the DLL is attaching to the process. If the entry-point function does not return TRUE, the system terminates the process and reports the error.

Finally, the system modifies the executable code of the process to provide starting addresses for the DLL functions. Like the rest of a program's code, DLL code is mapped into the address space of the process when the process starts up and it is loaded into memory only when needed. As a result, the PRELOAD and LOADONCALL code attributes used by .DEF files to control loading in previous versions of Windows no longer have meaning.

 

Explicit Linking

 

Most applications use implicit linking because it is the easiest linking method to use. However, there are times when explicit linking is necessary. Here are some common reasons to use explicit linking:

  1. The application does not know the name of a DLL that it will have to load until run time. For example, the application might need to obtain the name of the DLL and the exported functions from a configuration file.

  2. A process using implicit linking is terminated by the operating system if the DLL is not found at process startup. A process using explicit linking is not terminated in this situation and can attempt to recover from the error. For example, the process could notify the user of the error and have the user specify another path to the DLL.

  3. A process using implicit linking is also terminated if any of the DLLs it is linked to have a DllMain() function that fails. A process using explicit linking is not terminated in this situation.

  4. An application that implicitly links to many DLLs can be slow to start because Windows loads all of the DLLs when the application loads. To improve startup performance, an application can implicitly link to those DLLs needed immediately after loading and wait to explicitly link to the other DLLs when they are needed.

  5. Explicit linking eliminates the need to link the application with an import library. If changes in the DLL cause the export ordinals to change, applications using explicit linking do not have to re-link (assuming they are calling GetProcAddress() with a name of a function and not with an ordinal value), whereas applications using implicit linking must re-link to the new import library.

 

Here are two hazards of explicit linking to be aware of:

  1. If the DLL has a DllMain() entry point function, the operating system calls the function in the context of the thread that called LoadLibrary(). The entry-point function is not called if the DLL is already attached to the process because of a previous call to LoadLibrary() with no corresponding call to the FreeLibrary() function. Explicit linking can cause problems if the DLL is using a DllMain() function to perform initialization for each thread of a process because threads existing when LoadLibrary() (or AfxLoadLibrary()) is called will not be initialized.

  2. If a DLL declares static-extent data as __declspec(thread), it can cause a protection fault if explicitly linked. After the DLL is loaded with LoadLibrary(), it causes a protection fault whenever the code references this data. (Static-extent data includes both global and local static items.) Therefore, when you create a DLL, you should either avoid using thread-local storage, or inform DLL users about potential pitfalls (in case they attempt dynamic loading).

 

Creating a Resource-Only DLL

 

A resource-only DLL is a DLL that contains nothing but resources, such as icons, bitmaps, strings, and dialog boxes. Using a resource-only DLL is a good way to share the same set of resources among multiple programs. It is also a good way to provide an application with resources localized for multiple languages. To create a resource-only DLL, you create a new Win32 DLL (non-MFC) project and add your resources to the project.

  1. Select Win32 Project in the New Project dialog box and specify a DLL project type in the Win32 Project Wizard.

  2. Create a new resource script that contains the resources (such as a string or a menu) for the DLL and save the .rc file.

  3. On the Project menu, click Add Existing Item and insert the new .rc file into the project.

  4. Specify the /NOENTRY linker option. /NOENTRY prevents the linker from linking a reference to _main into the DLL; this option is required to create a resource-only DLL.

  5. Build the DLL.

 

The application that uses the resource-only DLL should call LoadLibrary() to explicitly link to the DLL. To access the resources, call the generic functions FindResource() and LoadResource(), which work on any kind of resource, or call one of the following resource-specific functions:

 

  1. FormatMessage()

  2. LoadAccelerators()

  3. LoadBitmap()

  4. LoadCursor()

  5. LoadIcon()

  6. LoadMenu()

  7. LoadString()

 

The application should call FreeLibrary() when it is finished using the resources.

 

Importing and Exporting

 

You can import public symbols into an application or export functions from a DLL using two methods:

  1. Use a module definition (.DEF) file when building the DLL.

  2. Use the keywords __declspec(dllimport) or __declspec(dllexport) in a function definition in the main application.

 

Using a .DEF file

 

A module-definition (.DEF) file is a text file containing one or more module statements that describe various attributes of a DLL. If you do not use __declspec(dllimport) or __declspec(dllexport) to export a DLL's functions, the DLL requires a .DEF file. You can use .DEF files to import into an application or to export from a DLL.

 

Using __declspec

 

The 32-bit edition of Visual C++ uses __declspec(dllimport) and __declspec(dllexport) to replace the __export keyword previously used in 16-bit versions of Visual C++. You do not need to use __declspec(dllimport) for your code to compile correctly, but doing so allows the compiler to generate better code. The compiler is able to generate better code because it knows for sure whether a function exists in a DLL or not, so the compiler can produce code that skips a level of indirection that would normally be present in a function call that crossed a DLL boundary. However, you must use __declspec(dllimport) in order to import variables used in a DLL. With the proper .DEF file EXPORTS section, __declspec(dllexport) is not required. __declspec(dllexport) was added to provide an easy way to export functions from an .EXE or .DLL without using a .DEF file. The Win32 Portable Executable (PE) format is designed to minimize the number of pages that must be touched to fix imports. To do this, it places all of the import addresses for any program in one place called the Import Address Table. This allows the loader to modify only one or two pages when accessing these imports.

 

Importing into an Application Using __declspec(dllimport)

 

A program that uses public symbols defined by a DLL is said to import them. When you create header files for applications that use your DLLs to build with, use __declspec(dllimport) on the declarations of the public symbols. The keyword __declspec(dllimport) works whether you export with .DEF files or with the __declspec(dllexport) keyword. To make your code more readable, define a macro for __declspec(dllimport) and then use the macro to declare each imported symbol:

#define DllImport   __declspec(dllimport)

 

DllImport int  j;

DllImport void func();

Using __declspec(dllimport) is optional on function declarations, but the compiler produces more efficient code if you use this keyword. However, you must use __declspec(dllimport) in order for the importing executable to access the DLL's public data symbols and objects. Note that the users of your DLL still need to link with an import library. You can use the same header file for both the DLL and the client application. To do this, use a special preprocessor symbol which indicates whether you are building the DLL or building the client application. For example:

 

#ifdef _EXPORTING

   #define CLASS_DECLSPEC    __declspec(dllexport)

#else

   #define CLASS_DECLSPEC    __declspec(dllimport)

#endif

 

class CLASS_DECLSPEC CExampleA : public CObject

{ ... class definition ... };

 

Exporting from a DLL

 

A .DLL file has a layout very similar to an .EXE file, with one important difference: a DLL file contains an exports table. The exports table contains the name of every function that the DLL exports to other executables. These functions are the entry points into the DLL; only the functions in the exports table can be accessed by other executables. Any other functions in the DLL are private to the DLL. The exports table of a DLL can be viewed by using the DUMPBIN tool (comes with your Visual Studio or you can try more powerful tool, PEBrowser) with the /EXPORTS option. You can export functions from a DLL using two methods:

  1. Create a module definition (.DEF) file and use the .DEF file when building the DLL. Use this approach if you want to export functions from your DLL by ordinal rather than by name.

  2. Use the keyword __declspec(dllexport) in the function's definition.

 

When exporting functions with either method, make sure to use the __stdcall calling convention. A module-definition (.DEF) file is a text file containing one or more module statements that describe various attributes of a DLL. If you are not using the __declspec(dllexport) keyword to export the DLL's functions, the DLL requires a .DEF file. A minimal .DEF file must contain the following module-definition statements:

  1. The first statement in the file must be the LIBRARY statement. This statement identifies the .DEF file as belonging to a DLL. The LIBRARY statement is followed by the name of the DLL. The linker places this name in the DLL's import library.

  2. The EXPORTS statement lists the names and, optionally, the ordinal values of the functions exported by the DLL. You assign the function an ordinal value by following the function's name with an at sign (@) and a number. When you specify ordinal values, they must be in the range 1 through N, where N is the number of functions exported by the DLL.

 

For example, a DLL that contains the code to implement a binary search tree might look like the following:

LIBRARY   BTREE

EXPORTS

   Insert   @1

   Delete   @2

   Member   @3

   Min   @4

If you use the MFC DLL Wizard to create an MFC DLL, the wizard creates a skeleton .DEF file for you and automatically adds it to your project. Add the names of the functions to be exported to this file. For non-MFC DLLs, you must create the .DEF file yourself and add it to your project. If you are exporting functions in a C++ file, you will have to either place the decorated names in the .DEF file or define your exported functions with standard C linkage by using extern "C". If you need to place the decorated names in the .DEF file, you can obtain them by using the DUMPBIN tool or by using the linker /MAP option. Note that the decorated names produced by the compiler are compiler specific. If you place the decorated names produced by the Visual C++ compiler into a .DEF file, applications that link to your DLL must also be built using the same version of Visual C++ so that the decorated names in the calling application match the exported names in the DLL's .DEF file. If you are building an extension DLL (MFC), and exporting using a .DEF file, place the following code at the beginning and end of your header files that contain the exported classes:

#undef AFX_DATA

#define AFX_DATA AFX_EXT_DATA

// <body of your header file>

#undef AFX_DATA

#define AFX_DATA

These lines ensure that MFC variables that are used internally or that are added to your classes are exported (or imported) from your extension DLL. For example, when deriving a class using DECLARE_DYNAMIC, the macro expands to add a CRuntimeClass member variable to your class. Leaving out these four lines may cause your DLL to compile or link incorrectly or cause an error when the client application links to the DLL.

When building the DLL, the linker uses the .DEF file to create an export (.EXP) file and an import library (.LIB) file. The linker then uses the export file to build the .DLL file. Executables that implicitly link to the DLL link to the import library when they are built. Note that MFC itself uses .DEF files to export functions and classes from the MFCx0.DLL.

 

Exporting from a DLL Using __declspec(dllexport)

 

Microsoft introduced __export in the 16-bit compiler version of Visual C++ to allow the compiler to generate the export names automatically and place them in a .LIB file. This .LIB file could then be used just like a static .LIB to link with a DLL. In the 32-bit compiler version, you can export data, functions, classes, or class member functions from a DLL using the __declspec(dllexport) keyword. __declspec(dllexport) adds the export directive to the object file so you don't need to use a .DEF file. This convenience is most apparent when trying to export decorated C++ function names. There is no standard specification for name decoration, so the name of an exported function may change between compiler versions. If you use __declspec(dllexport), recompiling the DLL and dependent .EXE files is necessary only to account for any naming convention changes. Many export directives, such as ordinals, NONAME, and PRIVATE, can be made only in a .DEF file, and there is no way to specify these attributes without a .DEF file. However, using __declspec(dllexport) in addition to using a .DEF file does not cause build errors. To export functions, the __declspec(dllexport) keyword must appear to the left of the calling-convention keyword, if a keyword is specified. For example:

__declspec(dllexport) void __stdcall WilBeExportedFunctionName(void);

And the real one may look something like this:

__declspec(dllexport) int mydll(LPTSTR lpszMsg)

To export all of the public data members and member functions in a class, the keyword must appear to the left of the class name as follows:

class __declspec(dllexport) CExampleExport : public CObject

{ ... class definition ... };

When building your DLL, you typically create a header file that contains the function prototypes and/or classes you are exporting, and add __declspec(dllexport) to the declarations in the header file. To make your code more readable, define a macro for __declspec(dllexport) and use the macro with each symbol you are exporting:

#define DllExport   __declspec(dllexport)

__declspec(dllexport) stores function names in the DLL's export table.

 

Initializing a DLL

 

Typically, your DLL has initialization code (such as allocating memory) that must execute when your DLL loads. When using Visual C++, where you add code to initialize your DLL depends on the kind of DLL you are building. If you don't need to add initialization or termination code, there's nothing special you have to do when building your DLL. If you need to initialize your DLL, the following table describes where to add your code.

 

Type of DLL

Where to add initialization and termination code

Regular DLL

In the DLL's CWinApp() object's InitInstance() and ExitInstance().

Extension DLL

In the DllMain() function generated by the MFC DLL Wizard.

Non-MFC DLL

In a function called DllMain() that you provide.

 

Table 1.

 

In Win32, all DLLs may contain an optional entry-point function (usually called DllMain()) that is called for both initialization and termination. This gives you an opportunity to allocate or release additional resources as needed. Windows calls the entry-point function in four situations: process attach, process detach, thread attach, and thread detach. The C run-time library provides an entry-point function called _DllMainCRTStartup(), and it calls DllMain(). Depending on the kind of DLL, either you should have a function called DllMain() in your source code or you should use the DllMain() provided in the MFC library.

 

Initializing Extension DLLs (For MFC programs)

 

Because extension DLLs do not have a CWinApp-derived object (as do regular DLLs), you should add your initialization and termination code to the DllMain() function that the MFC DLL Wizard generates. The wizard provides the following code for extension DLLs. In the code portion below, PROJNAME is a placeholder for the name of your project.

#include "stdafx.h"

#include <afxdllx.h>

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

static AFX_EXTENSION_MODULE PROJNAMEDLL = { NULL, NULL };

 

extern "C" int APIENTRY

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

{

   if (dwReason == DLL_PROCESS_ATTACH)

   {

      TRACE0("PROJNAME.DLL Initializing!\n");

      // Extension DLL one-time initialization

      AfxInitExtensionModule(PROJNAMEDLL, hInstance);

      // Insert this DLL into the resource chain

      new CDynLinkLibrary(Dll3DLL);

   }

   else if (dwReason == DLL_PROCESS_DETACH)

   {

      TRACE0("PROJNAME.DLL Terminating!\n");

   }

   return 1;   // ok

}

Creating a new CDynLinkLibrary object during initialization allows the extension DLL to export CRuntimeClass objects or resources to the client application. If you are going to use your extension DLL from one or more regular DLLs, you must export an initialization function that creates a CDynLinkLibrary object. That function must be called from each of the regular DLLs that use the extension DLL. An appropriate place to call this initialization function is in the InitInstance() member function of the regular DLL's CWinApp-derived object before using any of the extension DLL's exported classes or functions.

In the DllMain() that the MFC DLL Wizard generates, the call to AfxInitExtensionModule captures the module's run-time classes (CRuntimeClass structures) as well as its object factories (COleObjectFactory objects) for use when the CDynLinkLibrary object is created. You should check the return value of AfxInitExtensionModule; if a zero value is returned from AfxInitExtensionModule, return zero from your DllMain() function. If your extension DLL will be explicitly linked to an executable (meaning the executable calls AfxLoadLibrary to link to the DLL), you should add a call to AfxTermExtensionModule on DLL_PROCESS_DETACH. This function allows MFC to clean up the extension DLL when each process detaches from the extension DLL (which happens when the process exits, or when the DLL is unloaded as a result of a AfxFreeLibrary call). If your extension DLL will be linked implicitly to the application, the call to AfxTermExtensionModule is not necessary. Applications that explicitly link to extension DLLs must call AfxTermExtensionModule when freeing the DLL. They should also use AfxLoadLibrary and AfxFreeLibrary (instead of the Win32 functions LoadLibrary() and FreeLibrary()) if the application uses multiple threads. Using AfxLoadLibrary and AfxFreeLibrary ensures that the startup and shutdown code that executes when the extension DLL is loaded and unloaded does not corrupt the global MFC state. Because the MFCx0.DLL is fully initialized by the time DllMain is called, you can allocate memory and call MFC functions within DllMain (unlike the 16-bit version of MFC). Extension DLLs can take care of multithreading by handling the DLL_THREAD_ATTACH and DLL_THREAD_DETACH cases in the DllMain() function. These cases are passed to DllMain() when threads attach and detach from the DLL. Calling TlsAlloc() when a DLL is attaching allows the DLL to maintain thread local storage (TLS) indexes for every thread attached to the DLL.

Note that the header file AFXDLLX.H contains special definitions for structures used in extension DLLs, such as the definition for AFX_EXTENSION_MODULE and CDynLinkLibrary. You should include this header file in your extension DLL. Note that it is important that you neither define nor undefine any of the _AFX_NO_XXX macros in stdafx.h. Note that the sample contains an entry-point function called LibMain(), but you should name this function DllMain() so that it works with the MFC and C run-time libraries.

 

 

Initializing Non-MFC DLLs

 

To initialize non-MFC DLLs, your DLL source code must contain a function called DllMain(). The following code presents a basic skeleton showing what the definition of DllMain() might look like:

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)

{

    switch(ul_reason_for_call)

 {

    case DLL_PROCESS_ATTACH:

    ...

    case DLL_THREAD_ATTACH:

    ...

    case DLL_THREAD_DETACH:

    ...

    case DLL_PROCESS_DETACH:

    ...

    }

    return TRUE;

}

Run-Time Library Behavior

 

The C/C++ run-time library code performs the DLL startup sequence, eliminating the need to link with a separate module as was necessary in Windows 3.x. Included in the C/C++ run-time library code is the DLL entry-point function called _DllMainCRTStartup(). The _DllMainCRTStartup() function does several things, including calling _CRT_INIT, which initializes the C/C++ run-time library and invokes C++ constructors on static, non-local variables. Without this function, the run-time library would be left in an uninitialized state. _CRT_INIT is available for both a statically-linked CRT, or linking to the CRT DLL msvcrt.dll, from a user DLL.

While it is possible to specify another entry-point function using the /ENTRY: linker option, it is not recommended because your entry-point function would have to duplicate everything that _DllMainCRTStartup() does. When building DLLs in Visual C++, _DllMainCRTStartup() is linked in automatically and you do not need to specify an entry-point function using the /ENTRY: linker option.

In addition to initializing the C run-time library, _DllMainCRTStartup() calls a function called DllMain(). Depending on the kind of DLL you are building, Visual C++ provides DllMain() for you and it gets linked in so that _DllMainCRTStartup() always has something to call. In this way, if you do not need to initialize your DLL, there's nothing special you have to do when building your DLL. If you need to initialize your DLL, where you add your code depends on the kind of DLL you are writing.

The C/C++ run-time library code calls constructors and destructors on static, non-local variables. For example, in the following DLL source code, Equus and Sugar are two static, non-local objects of class CHorse, defined in HORSES.H. There is no function in source code that contains calls to a constructor function for CHorse or to the destructor function because these objects are defined outside of any function. Therefore, calls to these constructors and destructors must be performed by the run-time code. The run-time library code for applications also performs this function.

#include "horses.h"

 

CHorse  Equus(ARABIAN, MALE);

CHorse  Sugar(THOROUGHBRED, FEMALE);

 

BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)

...

Each time a new process attempts to use the DLL, the operating system creates a separate copy of the DLL's data: this is called "process attach." The run-time library code for the DLL calls the constructors for all of the global objects, if any, and then calls the DllMain() function with process attach selected. The opposite situation is process detach: the run-time library code calls DllMain() with process detach selected and then calls a list of termination functions, including atexit() functions, destructors for the global objects, and destructors for the static objects. Note that the order of events in process attach is the reverse of that in process detach.

The run-time library code is also called during thread attach and thread detach, but the run-time code does no initialization or termination on its own.

 

------------------------------------End C Run –Time & Dynamic Link Library Part 1------------------------------------

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. Microsoft Visual C++, online MSDN.

  2. Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.

  3. For Multibytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).

  4. Windows data types are Windows data types.

  5. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

 

 

|< Windows Processes & Threads: Synchronization 5 | Main | Dynamic Link Library, DLL 2 >| Site Index | Download |