Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Visual C++ 2005 (2006) [eng]-2

.pdf
Скачиваний:
74
Добавлен:
16.08.2013
Размер:
18.66 Mб
Скачать

Storing and Printing Documents

4.Implement a default constructor in your class.

5.Declare the Serialize() function in your class.

6.Implement the Serialize() function in your class to serialize all the data members.

The serialization process uses a CArchive object to perform the input and output. You use the CArchive object passed to the Serialize() function to serialize the data members of the class.

You have also seen how MFC supports output to a printer. To add to the basic printing capability provided by default, you can implement your own versions of the view class functions involved in printing a document. The principal roles of each of these functions are:

Function

Role

 

 

OnPreparePrinting()

Determine the number of pages in the document and call the

 

DoPreparePrinting() member of the view.

OnBeginPrinting()

Allocate the resources required in the printer device context that are

 

needed throughout the printing process, and determine the number

 

of pages in the document, where this is dependent on information

 

from the device context.

OnPrepareDC()

Set attributes in the printer device context as necessary.

OnPrint()

Print the document.

OnEndPrinting()

Delete any GDI resources created in OnBeginPrinting() and do any

 

other necessary cleanup.

 

 

Information relating to the printing process is stored in an object of type CPrintInfo that’s created by the framework. You can store additional information in the view, or in another object of your own. If you use your own class object, you can keep track of it by storing a pointer to it in the CPrintInfo object.

Exercises

You can download the source code for the examples in the book and the solutions to the following exercises from http://www.wrox.com.

1.Add some code to the OnPrint() function so that the page number is printed at the bottom of each page of the document in the form ‘Page n’. If you use the features of the CString class, you can do this with just three extra lines!

2.As a further enhancement to the CText class, change the implementation so that scaling works properly. (Hint — look up the CreatePointFont() function in the online help.)

899

18

Writing Your Own DLLs

Chapter 9 discussed how a C++/CLI class library is stored in a .dll file. Dynamic link libraries are also used extensively with native C++ applications. A complete discussion of DLLs in native C++ applications is outside the scope of a beginner’s book, but they are important enough to justify including an introductory chapter on them. In this chapter, you will learn about:

DLLs and how they work

When you should consider implementing a DLL

What varieties of DLL are possible and what they are used for

How you can extend MFC using a DLL

How to define what is accessible in a DLL

How to access the contents of a DLL in your programs

Understanding DLLs

Almost all programming languages support libraries of standard code modules for commonly used functions. In native C++ you’ve been using lots of functions that are stored in standard libraries, such as the ceil() function that you used in the previous chapter, which is declared in the <cmath> header. The code for this function is stored in a library file with the extension .lib, and when the executable module for the Sketcher program was created, the linker retrieved the code for this standard function from the library file and integrated a copy of it into the .exe file for the Sketcher program. If you write another program and use the same function, it will also have its own copy of the ceil() function. The ceil() function is statically linked to each application and is an integral part of each executable module, as illustrated in Figure 18-1.

Chapter 18

 

 

Static Library

 

 

Copy added to each program during linkedit

 

Library

 

 

function

 

 

ProgramA.exe

ProgramB.exe

ProgramC.exe

 

Library

 

 

function

 

Library

 

 

function

 

 

 

 

Library

 

 

function

Figure 18-1

 

 

Although this is a very convenient way of using a standard function with minimal effort on your part, it does have its disadvantages as a way for several concurrently executing programs to make use of the same function in the Windows environment. A statically linked standard function being used by more than one program concurrently is duplicated in memory for each program using it. This may not seem to matter much for the ceil() function, but some functions — input and output, for instance — are invariably common to most programs and are likely to occupy sizable chunks of memory. Having these statically linked would be extremely inefficient.

Another consideration is that a standard function from a static library may be linked into hundreds of programs in your system, so identical copies of the code for them will be occupying disk space in the .exe file for each program. For these reasons, an additional library facility is supported by Windows for standard

902

Writing Your Own DLLs

functions. It’s called a dynamic link library, and it’s usually abbreviated to DLL. This allows one copy of a function to be shared among several concurrently executing programs and avoids the need to incorporate a copy of the code for a library function into the executable module for a program that uses it.

How DLLs Work

A dynamic link library is a file containing a collection of modules that can be used by any number of different programs. The file usually has the extension .dll, but this isn’t obligatory. When naming a DLL, you can assign any extension that you like, but this can affect how they’re handled by Windows. Windows automatically loads dynamic link libraries that have the extension .dll. If they have some other extension, you will need to load them explicitly by adding code to do this to your program. Windows itself uses the extension .exe for some of its DLLs. You have likely seen the extensions .vbx and .ocx, which are applied to DLLs containing specific kinds of controls.

You might imagine that you have a choice about whether or not you use dynamic link libraries in your program, but you don’t. The Win32 API is used by every Windows program, and the API is implemented in a set of DLLs. DLLs are fundamental to Windows programming.

Connecting a function in a DLL to a program is achieved differently from the process used with a statically linked library, where the code is incorporated once and for all when the program is linked to generate the executable module. A function in a DLL is connected only to a program that uses it when the application is run, and this is done on each occasion the program is executed, as Figure 18-2 illustrates.

Figure 18-2 shows the sequence of events when three programs that use a function in a DLL are started successively and then all execute concurrently. No code from the DLL is included in the executable module of any of the programs. When one of the programs is executed, the program is loaded into memory, and if the DLL it uses isn’t already present, it too is loaded separately. The appropriate links between the program and the DLL are then established. If, when a program is loaded, the DLL is already there, all that needs to be done is to link the program to the required function in the DLL.

Note particularly that when your program calls a function in a DLL, Windows will automatically load the DLL into memory. Any program subsequently loaded into memory that uses the same DLL can use any of the capabilities provided by the same copy of the DLL because Windows recognizes that the library is already in memory and just establishes the links between it and the program. Windows keeps track of how many programs are using each DLL that is resident in memory so that the library remains in memory as long as at least one program is still using it. When a DLL is no longer used by any executing program, Windows automatically deletes it from memory.

MFC is provided in the form of a number of DLLs that your program can link to dynamically, as well as a library that your program can link to statically. By default, the Application wizard generates programs that link dynamically to the DLL form of MFC.

Having a function stored in a DLL introduces the possibility of changing the function without affecting the programs that use it. As long as the interface to the function in the DLL remains the same, the programs can use a new version of the function quite happily, without the need for recompiling or re-linking them. Unfortunately, this also has a downside: it’s easy to end up using the wrong version of a DLL with a program. This can be a particular problem with applications that install DLLs in the Windows System folder. Some commercial applications arbitrarily write the DLLs associated with the program to this folder without regard to the possibility of a DLL with the same name being overwritten. This can interfere with other applications that you have already installed and, in the worst case, can render them inoperable.

903

Chapter 18

 

 

 

 

 

 

2. .dll loaded

 

 

Library.dll

 

4. ProgramB loaded

ProgramC.exe

 

 

 

ProgramB.exe

 

 

 

ProgramA.exe

 

1. ProgramA loaded

 

 

 

 

 

 

6. ProgramC loaded

ProgramA

ProgramB

ProgramC

Library.dll

 

 

5. linkage to dll function

 

 

 

Function

 

3. linkage to dll function

 

 

 

 

7. linkage to dll function

Computer Memory

Figure 18-2

Run-Time Dynamic Linking

The DLL that you’ll create in this chapter is automatically loaded into memory when the program that uses it is loaded into memory for execution. This is referred to as load-time dynamic linking, or early binding, because the links to the functions used are established as soon as the program and DLL have been loaded into memory. This kind of operation was illustrated in Figure 18-2; however, this isn’t the only choice available. It’s also possible to cause a DLL to be loaded after execution of a program has started. This is called run-time dynamic linking or late binding. The sequence of operations that occurs with this is illustrated in Figure 18-3.

904

Writing Your Own DLLs

1.Program is loaded but no DLL is loaded. The program may use

any one of the three DLLs.

3.Library2.dll loaded at the request of the

Library1.dll program.

Library2.dll

Library3.dll

Program.exe

Program

2.At this point the program determines that Library2.dll is required and causes it to be loaded

4.The program obtains the address of a function from the DLL and uses it to call the function

Library2.dll

Function

Computer Memory

Figure 18-3

Run-time dynamic linking enables a program to defer linking of a DLL until it’s certain that the functions in a DLL are required. This allows you to write a program that can choose to load one or more of a number of DLLs based upon input to the program so that only those functions that are necessary are actually loaded into memory. In some circumstances, this can drastically reduce the amount of memory required to run a program.

A program implemented to use run-time dynamic linking calls the Windows API function LoadLibrary() to load the DLL when it’s required. The address of a function within the DLL can then be obtained using a function GetProcAddress(). When the program no longer has a need to use the

905

Chapter 18

DLL, it can detach itself from the DLL by calling the FreeLibrary() function. If no other program is using the DLL, it will be deleted from memory. I won’t be going into further details of how this works in this book.

Contents of a DLL

A dynamic link library isn’t limited to storing code for functions. You can also put resources into a DLL, including such things as bitmaps and fonts. The Solitaire game that comes with Windows uses a dynamic link library called Cards.dll which contains all the bitmap images of the cards and functions to manipulate them. If you wanted to write your own card game, you could conceivably use this DLL as a base and save yourself the trouble of creating all the bitmaps needed to represent the cards. Of course, to use it, you would need to know specifically which functions and resources are included in the DLL.

You can also define static global variables in a DLL, including C++ class objects, so that these can be accessed by programs using it. The constructors for global static class objects are called automatically when such objects are created. You should note that each program using a DLL gets its own copy of any static global objects defined in the DLL, even though they may not necessarily be used by a program. For global class objects, this involves the overhead of calling a constructor for each. You should, therefore, avoid introducing such objects into a DLL unless they are absolutely essential.

The DLL Interface

You can’t access just anything that’s contained in a DLL. Only items specifically identified as exported from a DLL are visible to the outside world. Functions, classes, global static variables, and resources can all be exported from a DLL, and those that are make up the interface to it. Anything that isn’t exported can’t be accessed from the outside. You’ll see how to export items from a DLL later in this chapter.

The DllMain() Function

Even though a DLL isn’t executable as an independent program, it does contain a special variety of the main() function, called DllMain(). This is called by Windows when the DLL is first loaded into memory to allow the DLL to do any necessary initialization before its contents are used. Windows will also call DllMain() just before it removes the DLL from memory to enable the DLL to clean up after itself if necessary. There are also other circumstances where DllMain() is called, but these situations are outside the scope of this book.

DLL Varieties

There are three different kinds of DLL that you can build with Visual C++ 2005 using MFC: an MFC extension DLL, a regular DLL with MFC statically linked, and a regular DLL with MFC dynamically linked.

MFC Extension DLL

You build this kind of DLL whenever it’s going to include classes derived from the MFC. Your derived classes in the DLL effectively extend the MFC. The MFC must be accessible in the environment where your DLL is used, so all the MFC classes are available together with your derived classes — hence the name “MFC extension DLL.” However, deriving your own classes from the MFC isn’t the only reason to

906

Writing Your Own DLLs

use an MFC extension DLL. If you’re writing a DLL that includes functions that pass pointers to MFC class objects to functions in a program using it or that receive such pointers from functions in the program, you must create it as an MFC extension DLL.

Accesses to classes in the MFC by an extension DLL are always resolved dynamically by linking to the shared version of MFC that is itself implemented in DLLs. An extension DLL is created using the shared DLL version of the MFC, so when you use an extension DLL, the shared version of MFC must be available. An MFC extension DLL can be used by a normal Application wizard generated application. It requires the option Use MFC in a Shared Dll to be selected under the General set of properties for the project, which you access through the Project > Properties menu option. This is the default selection with an Application wizard-generated program. Because of the fundamental nature of the shared version of the MFC in an extension DLL, an MFC extension DLL can’t be used by programs that are statically linked to MFC.

Regular DLL — Statically Linked to MFC

This is a DLL that uses MFC classes linked statically. Use of the DLL doesn’t require MFC to be available in the environment in which it is used, because the code for all the classes it uses is incorporated into the DLL. This bulks up the size of the DLL, but the big advantage is that this kind of DLL can be used by any Win32 program, regardless of whether or not it uses MFC.

Regular DLL — Dynamically Linked to MFC

This is a DLL that uses dynamically linked classes from MFC but doesn’t add classes of its own. This kind of DLL can be used by any Win32 program regardless of whether it uses MFC itself, but use of the DLL does require the MFC to be available in the environment.

You can use the Application wizard to build all three types of DLL that use MFC. You can also create a project for a DLL that doesn’t involve MFC at all, by creating a Win32 project type using the Win32 Project template and selecting DLL in the application settings for the project.

Deciding What to Put in a DLL

How do you decide when you should use a DLL? In most cases, the use of a DLL provides a solution to a particular kind of programming problem, so if you have the problem, a DLL can be the answer. The common denominator is often sharing code among a number of programs, but there are other instances where a DLL provides advantages. The kinds of circumstance where putting code or resources in a DLL provides a very convenient and efficient approach include the following:

You have a set of functions or resources on which you want to standardize and which you will use in several different programs. The DLL is a particularly good solution for managing these, especially if some of the programs using your standard facilities are likely to be executing concurrently.

You have a complex application that involves several programs and a lot of code but that has sets of functions or resources that may be shared among several of the programs in the application. Using a DLL for common functionality or common resources enables you to manage and develop these with a great deal of independence from the program modules that use them and can simplify program maintenance.

907

Chapter 18

You have developed a set of standard application-oriented classes derived from MFC that you anticipate using in several programs. By packaging the implementation of these classes in an extension DLL, you can make using them in several programs very straightforward, and in the process provide the possibility of being able to improve the internals of the classes without affecting the applications that use them.

You have developed a brilliant set of functions that provide an easy-to-use but amazingly powerful tool kit for an application area that just about everybody wants to dabble in. You can readily package your functions in a regular DLL and distribute them in this form.

There are also other circumstances where you may choose to use DLLs, such as when you want to be able to dynamically load and unload libraries, or to select different modules at run-time. You could even use them to ease the development and updating of your applications generally.

The best way of understanding how to use a DLL is to create one and try it out. Let’s do that now.

Writing DLLs

There are two aspects to writing a DLL that you’ll look at: how you actually write a DLL and how you define what’s to be accessible in the DLL to programs that use it. As a practical example of writing a DLL, you’ll create an extension DLL to add a set of application classes to the MFC. You’ll then extend this DLL by adding variables available to programs using it.

Writing and Using an Extension DLL

You can create an MFC extension DLL to contain the shape classes for the Sketcher application. Although this will not bring any major advantages to the program, it demonstrates how you can write an extension DLL without involving you in the overhead of entering a lot of new code.

The starting point is Application wizard, so create a new project by pressing Ctrl+Shift+N and choosing the project type as MFC and the template as MFC DLL, as shown in Figure 18-4.

This selection identifies that you are creating a project for an MFC-based DLL with the name ExtDLLExample. Click the OK button and select Application Settings in the next window that is displayed. The window looks as shown in Figure 18-5.

Here, you can see three radio buttons corresponding to the three types of MFC-based DLL that I discussed earlier. You should choose the third option, as shown in the figure.

The two checkboxes below the first group of three radio buttons allow you to include code to support Automation and Windows Sockets in the DLL. These are both advanced capabilities within a Windows program, so you don’t need either of them here. Automation provides the potential for hosting objects created and managed by one application inside another. Windows Sockets provides classes and functionality to enable your program to communicate over a network, but you won’t be getting into this as it’s beyond the scope of the book. You can click the Finish button and complete creation of the project.

908