Since the dawn of
time (or thereabouts), Windows operating systems have used dynamic link
libraries (DLLs) to support commonly used functions. Descendants of Windows,
including Windows NT and Windows 95 as well as OS/2, also depend on DLLs to
provide a large segment of their functionality.

In this chapter,
you look at several different aspects of using and creating DLLs. Here, you
will see how to

·Statically link to DLLs

·Load DLLs dynamically

·Create your own DLLs

·Create MFC extension DLLs

Using Dynamic
Link Libraries

For Windows
applications, it is virtually impossible to create an application that does not
use DLLs. All the Win32 API and countless other functions of the Win32
operating systems are contained in DLLs, although you may not have been aware
that the examples shown so far use DLLs at all.

In general, DLLs
are just collections of functions in a library. However, unlike their static
cousins (.lib files), DLLs are not linked directly into executable files by the
linker. Instead, only reference information is included in the executable file.
The bulk of the library code is then loaded at runtime. This allows different
processes to share libraries in memory, thus cutting down on the memory
required to run different applications that share many of the same libraries,
as well as keeping the size of EXEs manageable.

However, if your
library will be used by a single application, it might be more efficient to
create a simple, static link library. Of course, if your functions are to be
used in only one program, you might as well simply compile the source into your
one application.

In most cases, your
project will link to DLLs statically, or implicitly, at link-time. The
operating system then manages the process of loading the DLL for you at
runtime. However, you can also explicitly or dynamically load DLLs at runtime,
as you will see later in this chapter.

Import Libraries

When
statically linking to a DLL, you will specify a .lib file in the linker
options, either on the command line or in the Link page of Developer Studio's
project settings. However, the .lib file that you link to is not your average
static library. The .lib files that are used to implicitly link to DLLs are
known as import libraries. They do not contain the real meat of the code
contained in the library, but only references to each function exported by a
DLL file, which has all the good stuff in it. In general, this results in the
import libraries being much smaller than the DLL files. Yo 818b14i u will look at just
how these files are created later in this chapter; for now, let's look at some
of the other issues involved in linking to DLLs implicitly.

Calling Conventions

In the examples
that you have looked at so far, you haven't had to worry about the different
calling conventions that may be used to handle parameter passing and calls to
functions. That is because the libraries and headers provided in Visual C++
have taken care of this for you. However, if you will be using your own
libraries, or those from third parties, you will need to pay attention to this.
(These calling convention details apply to plain old static libraries as well.)

If this were a
perfect world, you wouldn't have to worry about calling conventions for
libraries—they would all be the same. However, this is not a perfect world and
a great deal of large-scale development is dependent on some sort of non-C++
library.

By default, Visual
C++ will use the C++ calling convention. This means that parameters will be
placed on the stack from right to left, the caller is responsible for removing
parameters from the stack when the call returns, and function names are mangled
(or decorated, depending on your political correctness).

Name mangling
allows the linker to differentiate between overloaded functions—that is,
functions with the same name but different argument lists. However, if you look
for a mangled function name in an old C library, you won't find it.

Although the rest
of the C calling convention is identical, C libraries do not mangle the names
of their functions, other than prepending an underscore (_) to the name.

If you plan to use
a C library in your C++ application, you will need to declare all the functions
from the C library as extern 'C', like this:

extern 'C' int MyOldCFunction(int myParm);

Declarations
for library functions are usually done in a header supplied with the library,
although most C libraries do not ship with a header designed for C++ use. In
this case, you add the extern 'C' modifier to each of the functions
in a copy of the header you will make for use with C++. This can be quite a
chore, so I generally use a shortcut: You can apply the extern 'C'
modifier to an entire block of code—namely, the #include that brings in the old
C header file. Thus, instead of the drudgery of modifying each function in a
header, you can do something like this:

extern 'C'

TIP

If you run into unresolved
external errors when linking, the linker will include the function name that
it is looking for at the end of the error message. If this name includes
strange characters (generated by C++ name mangling), other than a preceding
underscore (which is used in standard C linkages), the extern 'C'
modifier is not being used.

In programs for
older versions of Windows, the PASCAL calling convention was also used for
Windows API calls. In newer programs, you should use the WINAPI modifier, which
maps to _stdcall. Although this is not really a standard C or C++ calling
convention, it is the one used by Windows API calls. However, this is generally
all taken care of for you in the standard Windows headers.

Loading the DLL

When your
application starts, it will try to find all the DLL files that have been
implicitly linked to the application and map them into the process's memory
space. To find DLL files, the operating system will look in the following
places:

1.The directory from which the
EXE was run

2.The current directory for
the process

3.The Windows system directory

4.The Windows directory

5.The directories in the PATH
environment variable

NOTE

For Windows NT, the Windows
system directory in number 3 includes both the 32-bit Windows system
directory (usually SYSTEM32), which is searched first, and the 16-bit Windows
system directory (SYSTEM).

If the DLL is not
found, your application will display a dialog box, showing the user the DLL
that was not found and the path that was searched. The process then quietly
shuts down.

If the proper DLL
is found, it is then mapped into the process's memory space, where it will
remain until the process terminates. Your application can now call the
functions contained in the DLL without any further ado. If you want to
dynamically load and unload DLLs, you use the methods discussed next.

NOTE

Unlike Win3.1, global memory in
a DLL is not truly global—each process gets its own copy. Sharing memory
between processes is discussed in Chapter 8, 'Memory Management.'

Loading DLLs Dynamically

Occasionally, it is
useful to allow your application a bit more control over the loading of DLLs
than normal, implicit linking will allow. For instance, you may wish to specify
which DLL the user can use, or require the user to select options that affect
which DLL is to be used. The dynamic, or explicit, loading process allows you
to decide which DLLs will be loaded. This allows you to use several different
DLLs that provide the same functions, but work differently. For example, if you
develop a transport-independent communications module, your application could
decide at runtime whether to load the DLL for TCP/IP or NetBIOS.

::LoadLibrary()

The first thing you
do to load a DLL dynamically is to map the DLL module into the memory of your
process. This is done with the ::LoadLibrary() call, which takes a single
parameter—the name of the module to load. Your code should look something like
this:

HMODULE hMyDll;

hMyDll = LoadLibrary('MyLib');

if(hMyDll == NULL)

// Could not load DLL, handle the error…

Windows will assume
a default file extension of .dll if you don't specify an extension. In the
example, Windows will look for MyLib.dll. If you specify a path in the
filename, only that specific path is used to find the file; otherwise, Windows
will search for the file in the same way that it searches for implicitly linked
DLLs (as shown previously), starting with the directory the process's EXE
loaded from and continuing on through to the PATH.

Once Windows
locates the file, it will compare the full path of the file found to the full
path of DLLs already loaded in that process. If there is a match, the handle
for that library is returned, rather than having another copy loaded.

NOTE

LoadLibrary() may also be used
to load executable files into memory. The handle to the executable module can
then be used in calls to FindResource() or LoadResource().

If the file is
found and the DLL is loaded successfully, LoadLibrary() returns a handle to the
module. Hang on to this handle; you will be using it shortly. If an error
occurs, LoadLibrary() will return NULL.

NOTE

If the specified file is not
found, Windows will normally display a dialog to the user, stating that the
file was not found. However, if you are using MFC, it makes a call to
::SetErrorMode(), instructing Windows to simply return errors to the
application, rather than notifying the user before functions such as LoadLibrary()
return. This allows your applications to deal with errors as they see fit. If
you are not using MFC, you can call SetErrorMode() yourself.

::GetProcAddress()

Provided the DLL
has been loaded properly, you will next need to find the addresses for the
individual functions before you can use them. This can be done by calling
::GetProcAddress() with the handle returned by LoadLibrary() and the name of
the function. This name should be the name of the function as it is exported
from the DLL, as shown here:0

HMODULE hMyDll;

UINT (*pfnMyFunc)(char* strMyName);

hMyDll = ::LoadLibrary('MyLib');

ASSERT(hMyDll != NULL);

pfnMyFunc = (UINT (*)(char*))::GetProcAddress(hMyDll, 'MyFunc');

ASSERT(pfnMyFunc != NULL);

UINT nRc = (*pfnMyFunc)('Dave');

Alternatively, you
may also reference the function by the ordinal number it is exported with:

If the function is
not found, GetProcAddress() returns NULL. If the function is found, this will
return a generic pointer to a function. It is up to your application to make
sure that the pointer you use is defined to point to a function with the same
parameter list and return value as the function loaded from the DLL. In the
previous example, MyFunc takes a char pointer and returns a UINT. If there is a
mismatch in parameter lists, your stack will become corrupted when you make
calls through the pointer. This will almost certainly kill your process.

FreeLibrary()

When your
application is finished with a particular DLL, it may be unloaded from your
process with a call to ::FreeLibrary().

Loading MFC Extension DLLs

If you are loading
an MFC extension DLL, you should use AfxLoadLibrary() and AfxFreeLibrary()
instead of LoadLibrary() and FreeLibrary(). These functions are almost
identical to the Win32 API calls, but they will ensure that the MFC structures
initialized by an extension DLL are not corrupted by multiple threads. You will
see more about MFC extension DLLs later in this chapter.

Resource DLLs

You can also use
dynamic loading to load a resource DLL, which MFC will then use to load the
default resources for the application. To do this, you first make a call to
LoadLibrary() to map the DLL into memory; then you call AfxSetResourceHandle()
to let the framework know that it should get resources from the newly loaded
DLL, rather than those linked with the process's executable file. This can be
useful if you need to use different sets of resources, as in localization for
different languages.

Creating
Your Own DLLs

Now that you have
seen how DLLs can be used in your application, let's look at how you can create
your own. If you are developing real applications, you will most likely want to
try to put functions common to more than one process into DLLs so that Windows
can more efficiently manage the memory used.

The easiest way to
get started with building a DLL project is to use AppWizard to create a new
project for you. For simple DLLs, such as the ones you will see in this
chapter, you should use the DLL project type. This will create a new project
for you, with all the necessary project settings for building a DLL. You will
then have to add your own source files to the project manually.

If you plan to use
higher level MFC functionality, such as documents and views, or are creating an
OLE automation server, the MFC AppWizard (.dll) project type will do some extra
work for you. This project type will add the appropriate references to the MFC
libraries and add source files to declare and implement a CWinApp-derived
application object for your DLL.

TIP

It is often handy to first
create a top-level project for a tester for your DLL and then create the DLL
project as a subproject. This way, you can build the tester, and the DLL will
be automatically built if necessary.

DllMain()

Most DLLs are
simply a collection of loosely related functions that are exported for other
applications to use. In addition to the exported functions that are used, every
DLL includes a DllMain() function, which is used to initialize the DLL, as well
as to clean up when the DLL is unloaded. This function replaces the LibMain and
WEP functions used in previous versions of Windows. A sample skeleton for your
DllMain() function may look something like this:

BOOLWINAPI DllMain (HANDLE hInst,

DWORD dwReason,

LPVOID lpReserved)

// end switch

if(bAllWentWell)

return TRUE;

else

return FALSE;

} // end DllMain()

Your DllMain()
function may be called at several different times. The dwReason parameter will
tell you why DllMain() was called, from one of the following values:

When a process
first loads the DLL, DllMain() is called with a dwReason of DLL_PROCESS_ATTACH.
Whenever this process then creates a new thread, DllMain() is called with
DLL_THREAD_ATTACH. (This is not done for the first thread, because it will call
with DLL_PROCESS_ATTACH.)

When the process is
finished with the DLL, this function is called with dwReason of
DLL_PROCESS_DETACH. When a thread of the process (other than the first thread)
is destroyed, dwReason will be DLL_THREAD_DETACH.

Based on the value
of dwReason, you should do any per-process or per-thread initialization and
cleanup that your DLL requires, as shown in the previous example. In general,
per-process initialization deals with setting up any resources that are shared
by multiple threads, such as loading shared files or initializing libraries.
Per-thread initialization should be used for setting up things that are unique
to the thread, such as initializing thread local storage.

Your DLL may
include resources that are separate from those in the calling application. If
the functions in your DLL will be working with resources from the DLL, you will
certainly want to save the hInst handle somewhere safe. This handle will be
used in calls to load resources from the DLL.

The lpReserved
pointer is reserved for use by Windows, so your application shouldn't muck with
it. However, you may test the value of the pointer. If the DLL has been loaded
dynamically, this will be NULL; static loads will pass a non-NULL pointer.

If all goes well in
your DllMain(), it should return TRUE. If something goes wrong, you can return
FALSE to abort the operation.

NOTE

If your code does not supply its
own DllMain(), the compiler will add its own default version, which simply
returns TRUE.

Exporting Functions from Your DLL

In order for
applications to be able to use the functions in your DLL, each function must
have an entry in the DLL's exports table. To get the compiler to add an entry
to the exports table for a function, you have two options.

You
may export functions in your DLL by using the __declspec(dllexport) modifier in
front of all your function declarations. MFC also provides several macros that
evaluate to __declspec(dllexport), including AFX_CLASS_EXPORT, AFX_DATA_EXPORT,
and AFX_API_EXPORT. In the current version of Visual C++, these are all the
same, but they are provided to support future enhancements that may require
different handling.

The __declspec
method is not used as often as the second method, which involves module
definition (.def) files and gives you more control of the export process.

Module Definition Files

The syntax of .def
files in Visual C++ is pretty straightforward, particularly because most of the
more complicated options used in earlier versions of Windows no longer apply
under Win32. As you can see in the following simple example, the .def file
gives a name and description for the library, then a list of the functions to
be exported:

LIBRARY'MYDLL'

DESCRIPTION `MYDLL Example Dynamic Link Library'

EXPORTS

MyInitialize

MyCreate

MyConnect@3NONAME

MySend@4

MyReceive@5

MyDisconnect

You can specify an
ordinal number to a function by adding it to the exports line for the function
with an @. This ordinal can then be used in calls to GetProcAddress().
Actually, the compiler will assign ordinals to all exports, but the way this is
done is somewhat unpredictable if you do not specify ordinals explicitly.

In addition, you
will notice the NONAME option in the example. This tells the compiler not to
include the name of the function in the export table of the DLL. In some cases,
this can save a lot of space in the DLL file. Applications that use an import
library to link to the DLL implicitly will not notice a difference, because
implicit linking uses only ordinal numbers internally. However, applications
that load the DLL dynamically will need to pass the ordinal number, rather than
the function name, to GetProcAddress().

Exporting
Classes

Creating a .def
file to export even simple classes from your DLL can be a bit tricky. You will
have to explicitly export every function that may be used by an outside
application, including functions that you have not defined yourself.

If you take a look
at the map file generated by code that implements a class, you may be surprised
to see some of the functions listed there. These will include things such as
implicit constructors and destructors or the functions that MFC declares in
macros such as DECLARE_MESSAGE_MAP, as well as the functions that you implement
yourself.

Although
you can export each of these functions yourself, there is an easier way. If you
use the AFX_CLASS_EXPORT modifier macro in the declaration of your class, the
compiler will take care of exporting all necessary functions to allow
applications to use the class contained in the DLL.

DLL Memory Issues

Unlike static
libraries, which effectively become part of an application's code, dynamic link
libraries in pre-Win32 versions of Windows handled memory a bit differently.
Under Win16, DLL memory was kept outside a task's address space and provided
the ability to share memory between tasks with the global memory in a shared
DLL.

In Win32, the DLL's
memory is mapped into memory space of the loading process. Each process gets
its own copy of the 'global' memory for the DLL, which is
reinitialized when a new process loads the DLL. This means that the DLL cannot
be used to share memory between processes in the same way that Win16 allows.

However, you can
pull a few tricks with the DLL's data segment that will allow you to create a
single section of memory that is shared for all processes that use the DLL.

Suppose you have an
array of ints that you want to be used by all processes that loaded the DLL.
This could be done with the following code:

#pragma data_seg('.myseg')

int sharedInts[10];

// Other shared variables

#pragma data_seg()

#pragma comment(lib, 'msvcrt' '-section:.myseg,rws');

All variables
declared between the data_seg pragmas will be allocated in the segment named
.myseg. The comment pragma is not just a comment in the traditional sense;
rather it tells the C runtime library to mark your new section as readable,
writable, and shared.

Building the DLL

If you have created
a project with AppWizard, and properly updated the .def file for your DLL, it
should be all set to go. However, if you are creating your own make files, or
otherwise building without AppWizard projects, you should specify the /DLL
option to the linker. This will cause the linker to generate a DLL rather than
a stand-alone executable.

NOTE

If you are using a .def file
with the LIBRARY line in it, you do not need to explicitly declare the /DLL
option to the linker.

If you are using
MFC, there are also some special options that concern how your DLL will use MFC
libraries. These are covered in the next section on MFC DLLs.

DLLs and MFC

You are by no means
forced to use MFC in your DLLs, but there are several very important issues
involved with using MFC in your DLL.

There are two
levels at which your DLL can work with the MFC framework. The first of these
levels is the regular MFC DLL, which can use MFC but may not pass pointers to
MFC objects between the DLL and the application. The second level of MFC
support is implemented in an MFC extension DLL. This class of DLL requires some
extra work to set up, but will allow you to freely pass pointers to MFC objects
between the DLL and the application.

Regular MFC DLLs

Regular MFC DLLs
allow you to use MFC in your DLL, but they do not require that the calling
application also use MFC. In regular DLLs, you can use MFC in any way you see
fit, including deriving your own MFC-derived classes in the DLL and exporting
them for use in applications.

However, a regular
DLL cannot exchange pointers to MFC-derived classes with the application.

If you need to
exchange pointers to MFC objects or classes derived from MFC classes, across
the application-DLL boundary, you use the extension DLL shown in the next
section.

The regular DLL
replaces the USRDLL architecture used in previous implementations of MFC. (A
regular DLL that links to MFC statically works the same as the obsolete
USRDLL-type DLL.) The regular DLL is the architecture of choice for DLLs that
will be used by other programming environments, such as Visual Basic or
PowerBuilder.

To create a regular
MFC DLL with AppWizard, create a new project with the MFC AppWizard (.dll) and
choose one of the Regular DLL options in step 1 of 1. You may choose to link to
the MFC libraries either statically or dynamically. If you want to change how
your DLL links to MFC, you can do so with the combo box in the General page of
the project settings dialog.

In previous
versions of MFC, USRDLLs required special versions of the static MFC libraries.
This is no longer true, so go ahead and use the standard MFC static libs. In
addition, the USRDLL architecture would not allow you to link with the dynamic
MFC libraries. This is not true of the regular DLL type—you are free to use the
dynamic libraries for MFC.

Managing MFCState Information

Each module in an
MFC process keeps its own state information. This means that your DLL will have
different state information than the calling application. Because of this, any
functions that you export that will be called directly by the application's
code must tell MFC which state information to use. Before calling any MFC
routines in your regular MFC DLL that uses the dynamic MFC libraries, you use
the following line at the beginning of your exported functions:

AFX_MANAGE_STATE(AfxGetStaticModuleState());

This will set the
correct state information for the duration of the function in which it is
called.

MFC Extension DLLs

MFC allows you to
create DLLs that will appear to applications as if they were just that—extensions
to MFC, rather than a separate collection of functions. This sort of DLL can be
used to create your own MFC-derived classes for applications to use.

To enable your DLL
to freely pass pointers to MFC objects between the application and your DLL,
you need to create an MFC extension DLL. These DLLs must link to the dynamic
MFC libraries, as must any application that will use your MFC extension DLL. In
older versions of MFC, this type of DLL was called an AFXDLL.

To create a new MFC
extension DLL, it is easiest to start with the MFC AppWizard (.dll) and choose
the MFC Extension DLL option in step 1. This will create the new project for
you and set up all the proper project settings to create an MFC extension DLL.
In addition, it will supply you with a DllMain() for your DLL that does some
special processing required to initialize an extension DLL. You should also
notice that this type of DLL does not and should not have a CWinApp derived
object declared in it.

Initializing Extension DLLs

MFC extension DLLs
require some special initialization to fit into the MFC framework. To see how
this works, let's look at the DllMain() that is created by AppWizard for you:

static AFX_EXTENSION_MODULE MyExtDLL = ;

extern 'C' int APIENTRY

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

else if (dwReason == DLL_PROCESS_DETACH)

return 1;// ok

}

The most important
part of this function is the call to AfxInitExtenstionModule(), which
initializes your DLL to properly work in the MFC framework. It takes a handle
to the DLL's instance, which is passed into DllMain(), and an
AFX_EXTENSION_MODULE structure, which will hold information about your DLL for
MFC's use.

You will not have
to explicitly initialize your AFX_EXTENSION_MODULE structure, but you will need
to make sure you declare one. The constructor for CDynLinkLibrary will
initialize it for you. You must create a CDynLinkLibrary in your DLL—its
constructor will initialize the AFX_EXTENSION_MODULE structure and add your DLL
to the list of DLLs that MFC can work with.

Dynamic Loading of MFC Extension DLLs

Beginning with MFC
4.0, the framework supports dynamic loading and unloading of MFC DLLs, including
your extensions. To make this work properly for your DLL, you should add a call
to AfxTermExtensionModule() in your DllMain() when a process detaches. This
function should be passed the AFX_EXTENSION_MODULE that was used earlier in the
example. This functionality can be added by adding the following code to the
DllMain() shown previously:

if(dwReason == DLL_PROCESS_DETACH)

In addition,
remember that your new DLL is now an extension DLL and should be dynamically
loaded with the AfxLoadLibrary() and AfxFreeLibrary() calls rather than
LoadLibrary() and FreeLibrary().

Exports from
an Extension DLL

You need to export
any functions or classes that you intend for applications to be able to access
from your DLL. Although you could add all the mangled names to your DEF file
manually, you can use modifiers for your class and function declarations
provided by MFC for exporting from extension DLLs. These include AFX_EXT_CLASS
and AFX_EXT_API, as shown here:

class AFX_EXT_CLASS CMyClass : public CObject

void AFX_EXT_API MyFunc();

Debugging DLLs

Debugging DLLs can
be a bit different from debugging regular executables. This is mostly due to
the fact that a DLL does not run on its own, but rather must be called by
another application.

I mentioned briefly
that it is convenient to create a project for a test application, then create a
subproject of the test app for your DLL. This is a handy way to keep the
projects in sync. To debug your DLL, you will need to add it to the Additional
DLLs list in the Debug page of the test application's project settings. (If the
DLL is not in the path, you should specify the complete pathname.) Then when
you debug the test app, you can step into the DLL or set breakpoints or
whatever you normally would do in the debugger.

Occasionally, you
may find yourself developing a DLL that is called by another program for which
you don't have access to a debug version or source code. To allow you to debug
DLLs in these situations, you can specify an executable in the Executable for
debug session box on the Debug page of the project settings for your DLL
project. Then, when you choose Debug from the Build menu, this application will
start up, although any breakpoints you set in the DLL will stop execution and
allow you to use the debugger normally within your DLL.

Dumpbin.exe

If you are ever
curious about the internals of an executable image, such as an EXE or DLL file,
you may find the Dumpbin.exe tool to be quite useful. It is found in the
MsdevBin directory if it's not already in your path. Although this utility
has several different options, allowing you to dump just about anything about
an image that you would ever care to know, the most interesting options for DLLs
are the /IMPORT and /EXPORT flags. /IMPORT will show you all the functions that
the image will need to import and the files in which it expects to find them.
On the other hand, the /EXPORT options will show you all the functions exported
by an image and their ordinals.

Summary

In this chapter,
you took a look at dynamic link libraries, how they are used, and how they can
be created.

You saw how
applications can link with import libraries to implicitly link to DLLs, as well
as how to manage dynamic loading of DLLs at runtime with LoadLibrary() and
GetProcAddress().

You also explored
how to create your own DLLs, including how to create the projects, export
functions with DEF files or __declspec(dllexport), and initialize them in
DllMain().

In addition, you
learned how to create MFC extension DLLs that can work directly within the
framework of MFC, including the specifics of initializing and loading extension
DLLs.