after some timedll this time...

A programming tutorial on building the dynamic link libraries (DLL) using the C++ and MFC class library
Module 16:
C++, MFC & Dynamic
Link Libraries - DLL 1
Program examples compiled using Visual C++ 6.0 (MFC
6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub
topics for this Tutorial are listed below. If you think the terms used in this
DLL tutorial quite blur, you can try studying the
Dynamic Link Libraries - DLL
Fundamental DLL Theory
How Imports Are Matched to Exports
Implicit Linkage vs. Explicit
Symbolic Linkage vs. Ordinal
The DLL Entry Point: DllMain()
Instance Handles: Loading Resources
How the Client Program Finds
Debugging a DLL
MFC DLLs: Extension vs. Regular
The Shared MFC DLLs and the
Windows DLLs
MFC Extension DLLs: Exporting
The MFC Extension DLL Resource
Search Sequence
The MYMFC22A Example: An MFC
Extension DLL
Dynamic Link Libraries
If you want to write modular software, you'll
be very interested in dynamic link libraries (DLLs). You're probably
thinking that you've been writing modular software all along because
C++ classes are modular. But classes are
build-time modular, and DLLs
are runtime modular. Instead
of programming giant EXEs that you must rebuild and test each time you
make a change, you can build smaller DLL modules and test them individually.
You can, for example, put a C++ class in a DLL, which might be as small
as 12 KB after compiling and linking. Client programs can load and link
your DLL very quickly when they run. Microsoft Windows itself uses DLLs
for its major functions. DLLs are getting easier to write. Win32 has
greatly simplified the programming model, and there's more and better
support from AppWizard and the Microsoft Foundation Class (MFC) library.
This module shows you how to write DLLs in C++ and how to write client
programs that use DLLs. You'll explore how Win32 maps DLLs into your
processes, and you'll learn the differences between
MFC library regular DLLs and
MFC library extension DLLs.
You'll see examples of simple DLLs of each type as well as a more complex
DLL example that implements a custom control.
Fundamental DLL Theory
Before you look at the application framework's
support for DLLs, you must understand how Win32 integrates DLLs into
your process. You might want to review
to refresh
your knowledge of processes and virtual memory. Remember that a process
is a running instance of a program and that the program starts out as
an EXE file on disk.
Basically, a DLL is a file on disk (usually
with a DLL extension) consisting of global data, compiled functions,
and resources, that becomes part of your process.
It is compiled to load at a preferred
base address, and if there's no conflict with other DLLs,
the file gets mapped to the same virtual address in your process. The
DLL has various exported functions,
and the client program (the program that loaded the DLL in the first
place) imports those functions.
Windows matches up the imports and exports when it loads the DLL. Win32
DLLs allow exported global variables
as well as functions.
In Win32, each process gets its own copy
of the DLL's read/write global variables. If you want to share memory
among processes, you must either use a memory-mapped file or declare
a shared data section as described in Jeffrey Richter's Advanced Windows
(Microsoft Press, 1997). Whenever your DLL requests heap memory, that
memory is allocated from the client process's heap.
How Imports Are Matched
to Exports
A DLL contains a
table of exported functions.
These functions are identified to the outside world by their
symbolic names and (optionally)
by integers called ordinal numbers.
The function table also contains the
addresses of the functions
within the DLL. When the client program first loads the DLL, it doesn't know the
addresses of the functions it needs to call, but it does know the symbols or
ordinals. The dynamic linking process then builds a table that connects the
client's calls to the function addresses in the DLL. If you edit and rebuild the
DLL, you don't need to rebuild your client program unless you have changed
function names or parameter sequences. In a simple world, you'd have one EXE
file that imports functions from one or more DLLs. In the real world, many DLLs
call functions inside other DLLs. Thus, a particular DLL can have both exports
and imports.
&This is not a problem because the dynamic linkage process can handle
cross-dependencies. In the DLL code, you must explicitly declare your
exported functions like this:
__declspec(dllexport) int MyFunction(int n);
The alternative is to list your exported
functions in a module-definition
file, but that's usually more troublesome. On the client side, you need
to declare the corresponding imports like this:
__declspec(dllimport) int MyFunction(int n);
If you're using C++, the compiler generates a
decorated name for let say
MyFunction(),
that other languages can't use. These decorated names are the long names the
compiler invents based on class name, function name, and parameter types. They
are listed in the project's MAP
file. If you want to use the plain name
MyFunction(),
you have to write the declarations this way:
&C& __declspec(dllexport) int MyFunction(int n);
&C& __declspec(dllimport) int MyFunction(int n);
By default, the compiler uses the
argument passing convention, which means that the calling program pops the parameters
off the stack. Some client languages might require the
convention, which replaces the Pascal calling convention, and which means that
the called function pops the stack. Therefore, you might have to use the
__stdcall modifier in your DLL export declaration.
Just having import declarations isn't enough to make a client link to a DLL.
The client's project must specify the import
library (LIB) to the linker, and the client program must actually contain
a call to at least one of the DLL's imported
functions. That call statement must be in an executable path in the
Implicit Linkage vs. Explicit
The preceding section primarily describes
implicit linking, which is what you as a C++ programmer will probably
be using for your DLLs. When you build a DLL, the linker produces a companion
import LIB file, which contains every DLL's exported symbols and (optionally)
ordinals, but no code. The LIB file is a surrogate for the DLL that is added
to the client program's project. When you build (statically link) the client,
the imported symbols are matched to the exported symbols in the LIB file, and
those symbols (or ordinals) are bound into the EXE file. The LIB file also contains
the DLL filename (but not its full pathname), which gets stored inside the EXE
file. When the client is loaded, Windows finds and loads the DLL and then dynamically
links it by symbol or by ordinal.
Explicit linking
is more appropriate for interpreted languages such as Microsoft Visual Basic,
but you can use it from C++ if you need to. With explicit linking, you don't
instead, you call the Win32
LoadLibrary()
function, specifying the DLL's pathname as a parameter.
LoadLibrary()
returns an
parameter that you can use in a call to
GetProcAddress(),
which converts a symbol (or an ordinal) to an address inside the DLL. Suppose
you have a DLL that exports a function such as this:
&C& __declspec(dllexport) double SquareRoot(double d);
Here's an example of a client's explicit linkage
to the function:
double (SQRTPROC)(double);
VERIFY(hInstance
= ::LoadLibrary(&c:\\winnt\\system32\\mydll.dll&));
VERIFY(pFunction
= (SQRTPROC*)::GetProcAddress(hInstance, &SquareRoot&));
d = (*pFunction)(81.0);
Call the DLL function
With implicit linkage, all DLLs are loaded when
the client is loaded, but with explicit linkage, you can determine when DLLs
are loaded and unloaded. Explicit linkage allows you to determine at runtime
which DLLs to load. You could, for example, have one DLL with string resources
in English and another with string resources in Spanish. Your application would
load the appropriate DLL after the user chose a language.
Symbolic Linkage vs. Ordinal
In Win16, the more efficient ordinal linkage was
the preferred linkage option. In Win32, the symbolic linkage efficiency was
improved. Microsoft now recommends symbolic
over ordinal linkage. The DLL version
of the MFC library, however, uses ordinal linkage. A typical MFC program might
link to hundreds of functions in the MFC DLL. Ordinal linkage permits that program's
EXE file to be smaller because it does not have to contain the long symbolic
names of its imports. If you build your own DLL with ordinal linkage, you must
specify the ordinals in the project's DEF file, which doesn't have too many
other uses in the Win32 environment. If your exports are C++ functions, you
must use decorated names in the DEF file (or declare your functions with
extern &C&).
Here's a short extract from one of the MFC library DEF files:
?ReadList@CRecentFileList@@UAEXXZ @ 5458 NONAME
?ReadNameDictFromStream@CPropertySection@@QAEHPAUIStream@@@Z @ 5459 NONAME
?ReadObject@CArchive@@QAEPAVCObject@@PBUCRuntimeClass@@@Z @ 5460 NONAME
?ReadString@CArchive@@QAEHAAVCString@@@Z @ 5461 NONAME
?ReadString@CArchive@@QAEPADPADI@Z @ 5462 NONAME
?ReadString@CInternetFile@@UAEHAAVCString@@@Z @ 5463 NONAME
?ReadString@CInternetFile@@UAEPADPADI@Z @ 5464 NONAME
The numbers after the at (@)
symbols are the ordinals. Kind of makes you want to use symbolic linkage instead,
doesn't it?
The DLL Entry Point:
By default, the linker assigns the main entry point
_DllMainCRTStartup()
to your DLL. When Windows loads the DLL, it calls this function, which first
calls the constructors for global objects and then calls the global function
DllMain(), which you're supposed to write.
DllMain() is called not only when the DLL is
attached to the process but also when it is detached (and at other times as
well). Here is a skeleton
&C& int APIENTRY
DllMain(HINSTANCE
hInstance, DWORD dwReason, LPVOID lpReserved)
if (dwReason == DLL_PROCESS_ATTACH)
TRACE0(&MYMFC22A.DLL Initializing!\n&);
// Do initialization here
else if (dwReason == DLL_PROCESS_DETACH)
TRACE0(&MYMFC22A.DLL Terminating!\n&);
// Do cleanup here
return 1;&&
If you don't write a
DllMain() function
for your DLL, a do-nothing version is brought in from the runtime library.
function is also called when individual threads are started and terminated,
as indicated by the dwReason parameter. Richter's book tells you all you need
to know about this complex subject.
Instance Handles: Loading Resources
Each DLL in a process is identified by a unique
value. In addition, the process itself has an
value. All these instance handles are valid only within a particular process,
and they represent the starting virtual address of the DLL or EXE. In Win32,
values are the same and the types can be used interchangeably. The process (EXE)
instance handle is almost always
and the handle for a DLL loaded at the default base address is
If your program uses several DLLs, each will have a different
value, either because the DLLs had different base addresses specified at build
time or because the loader copied and relocated the DLL code.
Instance handles are particularly important for
loading resources. The Win32
FindResource()
function takes an
parameter. EXEs and DLLs can each have their own resources.
If you want a resource from the DLL, you specify the DLL's instance handle.
If you want a resource from the EXE file, you specify the EXE's instance handle.
How do you get an instance handle? If you want the
EXE's handle, you call the Win32
GetModuleHandle()
function with a NULL parameter. If you want the DLL's handle, you call the Win32
GetModuleHandle() function with the DLL name
as a parameter. Later you'll see that the MFC library has its own method of
loading resources by searching various modules in sequence.
How the Client Program Finds
If you link explicitly with
LoadLibrary(),
you can specify the DLL's full pathname. If you don't specify the pathname,
or if you link implicitly, Windows follows this search sequence to locate your
directory containing the EXE file.
process's current directory.
Windows system directory.
Windows directory.
directories listed in the
environment variable.
trap you can easily fall into. You build
a DLL as one project, copy the DLL file to the system directory, and then run
the DLL from a client program. So far, so good. Next you rebuild the DLL with
some changes, but you forget to copy the DLL file to the system directory. The
next time you run the client program, it loads the old version of the DLL. Be
Debugging a DLL
Visual C++ makes debugging a DLL easy. Just run
the debugger from the DLL project. The first time you do this, the debugger
asks for the pathname of the client EXE file.
Every time you &run& the DLL from the debugger after this, the debugger loads
the EXE, but the EXE uses the search sequence to find the DLL. This means that
you must either set the
environment variable to point to the DLL or copy the DLL to a directory in the
search sequence.
MFC DLLs: Extension vs. Regular
We've been looking at Win32 DLLs that have a
DllMain() function
and some exported functions. Now we'll move into the world of the MFC application
framework, which adds its own support layer on top of the Win32 basics. AppWizard
lets you build two kinds of DLLs
with MFC library support: extension DLLs
and regular DLLs. You must understand
the differences between these two types before you decide which one is best
for your needs. Of course, Visual C++ lets you build a pure Win32
DLL without the MFC library, just as it lets you build a Windows program without
the MFC library. This is an MFC-oriented book, however, so we'll ignore the
Win32 option here.
An extension DLL supports a
C++ interface. In other words, the
DLL can export whole classes and the client can construct objects of those classes
or derive classes from them. An extension DLL dynamically links to the code
in the DLL version of the MFC library. Therefore, an extension DLL requires
that your client program be dynamically linked
to the MFC library (the AppWizard default) and that both the client program
and the extension DLL be synchronized to the same version of the MFC DLLs (mfc42.dll, mfc42d.dll,
and so on). Extension DLL you can build a simple extension
DLL with a size of 10 KB, which loads quickly.
If you need a DLL that can be loaded by any Win32
programming environment (including Visual Basic version 6.0), you should use
a regular DLL. A big restriction
here is that the regular DLL can export only
C-style functions. It can't export C++ classes, member functions,
or overloaded functions because every C++ compiler has its own method of decorating
names. You can, however, use C++ classes (and MFC library classes, in particular)
inside your regular DLL.
When you build an MFC regular DLL, you can choose
to statically link or dynamically link to the MFC library. If you choose static
linking, your DLL will include a copy of all the MFC library code it needs and
will thus be self-contained. A typical Release-build
statically linked regular DLL is about 144 KB in size. If you choose
dynamic linking, the size drops
to about 17 KB but you'll have to ensure that the proper MFC DLLs are present
on the target machine. That's no problem if the client program is already dynamically
linked to the same version of the MFC library. When you tell AppWizard what
kind of DLL or EXE you want, compiler
constants are set as shown in the following table.
Dynamically Linked
to Shared MFC Library
Statically Linked*
to MFC Library
Regular DLL
Extension DLL
unsupported option
Client EXE
no constants defined
* Visual C++ Learning Edition does not support the
static linking option.
If you look inside the MFC source code and header
files, you'll see a ton of
statements for these constants. This means that the library code is compiled
quite differently depending on the kind of project you're producing.
The Shared MFC DLLs and the
Windows DLLs
If you build a Windows Debug target with the shared
MFC DLL option, your program is dynamically linked to one or more of these (ANSI)
Description
mfc42d.dll
Core MFC classes.
mfco42d.dll
ActiveX (OLE) classes.
mfcd42d.dll
Database classes (ODBC and DAO).
mfcn42d.dll
When you build a
Release target, your program is dynamically
linked to mfc42.dll only. Linkage
to these MFC DLLs is implicit via import libraries. You might assume implicit
linkage to the ActiveX and ODBC DLLs in Windows, in which case you would expect
all these DLLs to be linked to your Release-build client when it loads, regardless
of whether it uses ActiveX or ODBC features. However, this is not what happens.
Through some creative thinking, MFC loads the ActiveX and ODBC DLLs explicitly
(by calling
LoadLibrary())
when one of their functions is first called. Your client application thus loads
only the DLLs it needs.
MFC Extension DLLs: Exporting
If your extension DLL contains only exported C++
classes, you'll have an easy time building and using it. The steps for building
the MYMFC22A example show you how to tell AppWizard that you're building an
extension DLL skeleton. That skeleton has only the
function. You simply add your own C++ classes to the project. There's only one
special thing you must do. You must add the macro
AFX_EXT_CLASS
to the class declaration, as shown here:
AFX_EXT_CLASS CStudent : public CObject
This modification goes into the
H file that's part of the DLL project,
and it also goes into the H file that client programs use. In other words, the
H files are exactly the same for both client and DLL. The macro generates different
code depending on the situation, it exports the class in the DLL and imports
the class in the client.
The MFC Extension DLL Resource
Search Sequence
If you build a dynamically linked MFC client application,
many of the MFC library's standard resources (error message strings, print preview
dialog templates, and so on) are stored in the MFC DLLs (mfc42.dll, mfco42.dll,
and so on), but your application has its own resources too. When you call an
MFC function such as CString::LoadString or
CBitmap::LoadBitmap,
the framework steps in and searches first the EXE file's resources and then
the MFC DLL's resources.
If your program includes an extension DLL and your
EXE needs a resource, the search sequence is first the EXE file, then the extension
DLL, and then the MFC DLLs. If you have a string resource ID, for example, that
is unique among all resources, the MFC library will find it. If you have duplicate
string IDs in your EXE file and your extension DLL file, the MFC library loads
the string in the EXE file.
If the extension DLL loads a resource, the sequence
is first the extension DLL, then the MFC DLLs, and then the EXE.
You can change the search sequence if you need to.
Suppose you want your EXE code to search the extension DLL's resources first.
Use code such as this:
hInstResourceClient = AfxGetResourceHandle();
Use DLL's instance handle
AfxSetResourceHandle(::GetModuleHandle(&my_dll_file_name.dll&));
strRes.LoadString(IDS_MYSTRING);
Restore client's instance handle
AfxSetResourceHandle(hInstResourceClient);
You can't use
AfxGetInstanceHandle()
instead of
::GetModuleHandle().
In an extension DLL,
AfxGetInstanceHandle()
returns the EXE's instance handle, not the DLL's handle.
The MYMFC22A Example: An MFC
Extension DLL
This example makes an extension DLL out of the
CPersistentFrame
class you saw in
. First you'll build
the mymfc22A.dll file, and then
you'll use it in a test client program, MYMFC22B.
Here are the steps for building the MYMFC22A example:
Run AppWizard to produce \mfcproject\mymfc22A. Choose
New from Visual C++'s
File menu, and then click on the
Projects tab as usual. Instead
of selecting MFC AppWizard (exe),
choose MFC AppWizard (dll), as
shown here.
--------------------------------------------------------------
Figure 1: AppWizard new DLL project creation dialog.
In this example, only one AppWizard screen appears.
Choose MFC Extension DLL, as shown
Figure 2: The only step 1 of 1 DLL project.
Figure 3: MYMFC22A DLL project summary.
Examine the mymfc22A.cpp
file. AppWizard generates the following code, which includes the
DllMain() function:
mymfc22A.cpp : Defines the initialization routines for the DLL.
&stdafx.h&
&afxdllx.h&
new DEBUG_NEW
char THIS_FILE[] =
AFX_EXTENSION_MODULE Mymfc22ADLL = { NULL, NULL };
&C& int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
// Remove this if you use lpReserved
&&&&&& UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
&&&&&&&&&&&&&
TRACE0(&MYMFC22A.DLL Initializing!\n&);
&&&&&&&&&&&&&
// Extension DLL one-time initialization
&&&&&&&&&&&&&
if (!AfxInitExtensionModule(Mymfc22ADLL,
hInstance))
&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&
// Insert this DLL into the resource
&&&&&&&&&&&&&
// NOTE: If this Extension DLL
is being implicitly linked to by
&&&&&&&&&&&&&
// an MFC Regular DLL (such as
an ActiveX Control)
&&&&&&&&&&&&&
// instead of an MFC application,
then you will want to
&&&&&&&&&&&&&
// remove this line from DllMain
and put it in a separate
&&&&&&&&&&&&&
// function exported from this
Extension DLL.& The Regular DLL
&&&&&&&&&&&&&
// that uses this Extension DLL
should then explicitly call that
&&&&&&&&&&&&&
// function to initialize this
Extension DLL.& Otherwise,
&&&&&&&&&&&&&
// the CDynLinkLibrary object
will not be attached to the
&&&&&&&&&&&&&
// Regular DLL's resource chain,
and serious problems will
&&&&&&&&&&&&&
// result.
&&&&&&&&&&&&&
new CDynLinkLibrary(Mymfc22ADLL);
if (dwReason == DLL_PROCESS_DETACH)
&&&&&&&&&&&&&
TRACE0(&MYMFC22A.DLL Terminating!\n&);
&&&&&&&&&&&&&
// Terminate the library before
destructors are called
&&&&&&&&&&&&&
AfxTermExtensionModule(Mymfc22ADLL);
return 1;&&
Insert the
CPersistentFrame
class into the project. Choose Add To Project
from the Project menu, and then
choose Components And Controls
from the submenu.
Figure 4: Inserting
CPersistentFrame
class into the MYMFC22A project.
Locate the file
Persistent Frame.ogx that you created
. Click the
Insert button to insert the class
into the current project.
Figure 5: Our previous
CPersistentFrame
class that we stored in the gallery.
If you don't want to use the
OGX component, you can copy the files
Persist.h and
Persist.cpp into your project directory
and add them to the project by choosing Add To Project from the Visual
C++ Project menu.
Figure 6: Adding
Persist.h and
Persist.cpp files manually to the MYMFC22A project.
Figure 7: Selecting
Persist.h and
Persist.cpp files.
Edit the persist.h
file. Modify the line:
CPersistentFrame : public CFrameWnd
AFX_EXT_CLASS CPersistentFrame : public CFrameWnd
Listing 1.
Build the project
copy the DLL file. Copy the file
mymfc22A.dll from the \myproject\mymfc22A\Debug
directory to your system directory (\Windows\System
or \Winnt\System32).
Figure 8: The generated DLL file, copied to the
Windows system directory.
System directory for Win Xp Pro is shown below (or
C:\WINDOWS\system32).
Figure 9: Copy the previous generated DLL file to
the Windows directory, so that it can be found by applications from any path.
Continue on next module...part
reading and digging:
- latest version.
and Multibyte character set:}

我要回帖

更多关于 after some time 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信