CATEGORII DOCUMENTE |
Asp | Autocad | C | Dot net | Excel | Fox pro | Html | Java |
Linux | Mathcad | Photoshop | Php | Sql | Visual studio | Windows | Xml |
Creating Your Own Classes and Modules
* Designing Classes
* Encapsulation
* Inheritance
* Visual C++ Class Types
* Creating Library Modules
* Using Library Modules
* Creating the Library Module
* Creating a Test Application
* Updating the Library Module
* Summary
* Q&A
* Workshop
* Quiz
* Exercise
Sometimes you need to build a set of application functionality that will be used in an application that another programmer is working on. Maybe the functionality will be used in a number of applications. Another possibility is that you want to separate some functionality from the rest of the application for organizational purposes. You might develop this separate set of functionality and then give a copy of the code to your friend to include in his application, but then every time you make any changes to your set of functionality, it has to be reincorporated into the other set of application code. It would be much more practical if you could give a compiled version of your functionality to the other programmer so that every time you updated your part, all you had to hand over was a new compiled file. The new file could just replace the previous version, without having to make any changes to the other programmer's code.
Well, it is possible to place your set of functionality into a self-contained compiled file, link it into another programmer's application, and avoid adding any new files to the finished application. Today, you will learn
* How to design your own classes.
* How to create compiled modules that can be linked into other applications.
* How to include these modules into an application.
Designing Classes
You've already designed and built your own classes over the past few days, so the basics of creating a new class is not a new topic. Why did you create these classes? Each of the new classes that you created encapsulated a set of functionality that acted as a self-contained unit. These units consisted of both data and functionality that worked together to define the object.
Encapsulation
Object-oriented software design is the practice of designing software in the same way that everything else in the world is designed. For instance, you can consider your car built from a collection of objects: the engine, the body, the suspension, and so on. Each of these objects consists of many other objects. For instance, the engine contains either the carburetor or the fuel injectors, the combustion chamber and pistons, the starter, the alternator, the drive chain, and so on. Once again, each of these objects consists of even more objects.
Each of these objects has a function that it performs. Each of these objects knows how to perform its own functions with little, if any, knowledge of how the other objects perform their functions. Each of the objects knows how it interacts with the other objects and how they are connected to the other objects, but that's about all they know about the other objects. How each of these objects work internally is hidden from the other objects. The brakes on your car don't know anything about how the transmission works, but if you've got an automatic transmission, the brakes do know how to tell the transmission that they are being applied, and the transmission decides how to react to this information.
You need to approach designing new classes for your applications in the same way. The rest of the application objects do not need to know how your objects work; they only need to know how to interact with your objects. This principle, called encapsulation, is one of the basic principles of object-oriented software.
Inheritance
Another key principle of object-oriented software design is the concept of inheritance. An object can be inherited from another object. The descendent object inherits all the existing functionality of the base object. This allows you to define the descendent object in terms of how it's different from the base object.
Let's look at how this could work with a thermostat. Suppose you had a basic thermostat that you could use in just about any setting. You could set the temperature for it to maintain, and it would turn on the heating or the air-conditioning as needed to maintain that temperature. Now let's say you needed to create a thermostat for use in a freezer. You could start from scratch and build a customized thermostat, or you could take your existing thermostat and specify how the freezer version differs from the original. These differences might include that it's limited to turning on the air conditioning and could never turn on the heater. You would probably also put a strict limit on the range of temperatures to which the thermostat could be set, such as around and below 32[infinity] Fahrenheit, or 0[infinity] Celsius. Likewise, if you needed a thermostat for an office building, you would probably want to limit the temperature range to what is normally comfortable for people and not allow the temperature to be set to an extremely cold or hot setting.
With inheritance in creating your own classes, this method just described represents the same principle that you want to apply. If possible, you should start with an existing C++ class that has the basic functionality that you need and then program how your class is different from the base class that you inherited from. You have the ability to add new data elements, extend existing functionality, or override existing functionality, as you see fit.
Visual C++ Class Types
In most application projects, when you are creating a new class, you have a few options on the type of class that you are creating. These options are
* Generic class
* MFC class
* Form class
Which of these types of classes you choose to create depends on your needs and what your class will be doing. It also depends on whether your class needs to descend from any of the MFC classes.
Generic Class
You use a generic class for creating a class that is inherited from a class you have already created. This class type is intended for creating classes that are not inherited from any MFC classes (although you have already seen where you need to use it to create classes that are based on MFC classes). If you want to create a more specialized version of the CLine class, for instance, a CRedLine class, that only drew in red, you create it as a generic class because it's inherited from another class that you created.
When you create a generic class, the New Class Wizard tries to locate the declaration of the base class (the header file with the class declared). If it cannot find the appropriate header file, it tells you that you might need to make sure that the header file with the base class definition is included in the project. If the base class happens to be an MFC class that is not accessible as an MFC class (such as CObject), then you can ignore this warning because the correct header file is already part of the project.
MFC Class
If you want to make a reusable class that is based on an existing MFC class, such as an edit box that automatically formats numbers as currency, you want to create an MFC class. The MFC class type is for creating new classes that are inherited from existing MFC classes.
Form Class
The form class is a specialized type of MFC class. You need to create this type of class if you are creating a new form style window. It can be a dialog, form view, or database view class. This new class will be associated with a document class for use with the view class. If you are building a database application, you will probably create a number of this style of classes.
Creating Library Modules
When you create new classes for your application, they might be usable in other applications as well. Often, with a little thought and effort, classes you create can be made flexible enough so that they could be used in other applications. When this is the case, you need some way of packaging the classes for other applications without having to hand over all your source code. This is the issue that library modules address. They allow you to compile your classes and modules into a compiled object code library that can be linked into any other Visual C++ application.
Library modules were one of the first means available to provide compiled code to other programmers for use in their applications. The code is combined with the rest of the application code by the linker as the final step in the compilation process. Library modules are still a viable means of sharing modules with other developers. All the developer needs is the library (.lib) file and the appropriate header files that show all the exposed classes, methods, functions, and variables, which the other programmer can access and use. The easiest way to do this is to provide the same header file that you used to create the library file, but you can also edit the header so that only the parts that other programmers need are included.
By using library files to share your modules with other programmers, you are arranging that your part of the application is included in the same executable file as the rest of the application. Your modules are not included in a separate file, such as a DLL or ActiveX control. This results in one less file to be distributed with the application. It also means that if you make any changes to the module, fix any bugs, or enhance any functionality, then the applications that use your module must be relinked. Using library files has a slight disadvantage to creating DLLs, where you may be able to just distribute the new DLL without having to make any changes to the application, but you'll learn all about that tomorrow.
Using Library Modules
To get a good idea of how to use library modules, it's helpful to create a library module, use it in another application, and then make some modifications to the library module. For today's sample application, you'll create a module that generates a random drawing on the window space specified. It'll be able to save and restore any of these drawings. You'll then use this module in an SDI application, where every time a new document is specified, a new drawing is generated. The initial module will only use eight colors and will generate only a limited number of line sequences. Later, you'll modify the module so that it will generate any number of colors and will generate a larger number of line sequences.
Creating the Library Module
To create a library module project, you need to specify in the New dialog that you want to create a Win32 Static Library, as shown in Figure 16.1. This tells Visual C++ that the output from the project compilation will be a library module instead of an executable application. From there, all you have to do is define the classes and add the code. You have the options of including support for MFC and using precompiled headers in your project, as in Figure 16.2, the only step in the Project Wizard.
The library that you will create for today's sample application will consist of two classes. The first class will be the CLine class that you first created on Day 10, 'Creating Single Document Interface Applications.' The second class will be the class that creates the random drawings on the drawing surface. This class will contain an object array of the CLine objects that it will create and populate with each of the drawing efforts. This second class will also need functionality to save and restore the drawing, as well as to delete the existing drawing so that a new drawing can be started. It will need to know the dimensions of the drawing area so that it can generate a drawing that will fit in the drawing area. Once you create this module, you'll take a look at how you can use this module in an application project.
FIGURE 16.1. Specifying a library module project.
FIGURE 16.2. Specifying project support options.
Creating a Library Project
To start the library project for today's example, you need to create a new project, specifying that the project is a Win32 Static Library project. Give the project a suitable name and click OK to create the project.
For today's sample project, specify on the one wizard step to include both MFC and precompiled header support. Although the precompiled header support is not necessary, it will speed up most compiles that you perform while building the module.
Once you create your module project, you'll find yourself working with a project that has no classes. You've got a blank slate from which you can create whatever type of module you need.
For your sample project, because you already have the CLine class built, copy it from the Day 10 project area into the project directory for today's project. Add both the header and source code file to today's project by choosing Project | Add To Project Files. Once you add both of these files to the project, you should see the CLine class appear in the Class View of your project.
Defining the Classes
Now that you've got a basic library module project ready to go, it's time to begin adding the meat of the module. Using the CLine class is an easy way of reusing some functionality that you created earlier in another setting. However, the real functionality of this module will be in its ability to generate random drawings, or squiggles. For this functionality, you'll need to create a new class.
To start this new class, add a new class to the project by selecting New Class from the pop-up menu in the Class View tab. The first thing that you'll notice in the New Class dialog is that you are limited to creating generic classes. Because you are creating a static library that will be linked into the application, Visual C++ is making some assumptions about the type of class that you want to create. Because this is not an MFC project, even though MFC support is included, you are prevented from creating a new MFC or form class. If you need to inherit a new class from an MFC class, you have to add it as if it were a generic class.
Use the New Class dialog to create your new class. Give the class a name that reflects its functionality, such as CModArt, and specify that it's derived from the CObject class as public. You'll receive the same warning that the base class header file cannot be found, but because you specified that MFC support should be included, you can ignore that message.
Once you create your class, you need to add a couple of variables to the class. First, you need somewhere to hold all the lines that will make up the drawing, so you'll add an object array. Second, you need to know the area of the drawing surface, so you'll want a CRect to hold the drawing area specification. You can add both of these variables to your new class using the types and names in Table 16.1.
TABLE 16.1. CModArt VARIABLES.
Type Name Access
static const COLORREF m_crColors[8] Public
CRect m_rDrawArea Private
CObArray m_oaLines Private
Setting the Drawing Area
Before you can draw anything, you need to know the area that you have to draw within. You can add a public function to your class that will copy the passed in CRect to the member CRect variable. To add this function to your project, add a new member function to your new class, specifying the type as void, the declaration as SetRect(CRect rDrawArea), and the access as public. Edit the function as in Listing 16.1.
LISTING 16.1. THE CModArt SetRect FUNCTION.
1: void CModArt::SetRect(CRect rDrawArea)
Creating a New Drawing
One of the key pieces to this module is the ability to generate random squiggles that appear on the drawing area. By generating a whole series of these squiggles, your module will be able to create an entire drawing. Starting with the single squiggle, you can design a function that generates one squiggle and then calls this function a number of times to generate the entire drawing.
This first function, the squiggle generator, needs to determine how many lines will be in the squiggle. It needs to determine the color and width of the pen to be used when drawing the squiggle. It also needs to determine the starting point for the squiggle. From this point, it could loop through the appropriate number of lines, generating a new destination to continue the squiggle from the previous destination point.
To add this functionality to your project, add a new member function to the drawing class. Specify the function type as void, the definition as NewLine, and the access as private because this function will only be called by the master loop that is determining how many of these squiggles will be in the final drawing. Edit the new function with the code in Listing 16.2.
LISTING 16.2. THE CModArt NewLine FUNCTION.
1: void CModArt::NewLine()
// Did we run into a memory exception?
catch (CMemoryException* perr)
// Delete the exception object
perr->Delete();
}
// Set the starting point to the end point
pFrom = pTo;
}
}
In this function, the first thing that you did was get the area that you had available for drawing with the following three lines:
m_rDrawArea.NormalizeRect();
int lWidth = m_rDrawArea.Width();
int lHeight = m_rDrawArea.Height();
In the first of these lines, you normalized the rectangle. This is necessary to guarantee that the width and height returned in the next two lines are both positive values. Because of the coordinate system used in Windows, getting the width by subtracting the left-side position from the right-side position can result in a negative number. The same can happen with the height. By normalizing the rectangle, you are guaranteeing that you'll get positive results for these two values.
Once you determined the drawing area, you determined the number of line segments you would use in this squiggle:
lNumLines = rand() % 100;
The rand function is capable of returning numbers in a wide range. By getting the modulus of 100, you are guaranteeing that the resulting number will be between 0 and 100. This is a common technique for generating random numbers within a certain range, using the modulus function with the upper limit of the value range (or the upper limit minus the lower limit, if the lower limit is not equal to 0, and then adding the lower limit to the resulting number). You use the same technique to determine the color, width, and starting position for the squiggle:
nCurColor = rand() % 8;
nCurWidth = (rand() % 8) + 1;
pFrom.x = (rand() % lWidth) + m_rDrawArea.left;
pFrom.y = (rand() % lHeight) + m_rDrawArea.top;
Notice how when you were determining the starting position, you added the left and top of the drawing area to the position that you generated. This guarantees that the starting position is within the drawing area. Once you enter the loop, generating all the line segments in the squiggle, you limit the available area for the next destination within 10 of the current position:
pTo.x = ((rand() % 20) - 10) + pFrom.x;
pTo.y = ((rand() % 20) - 10) + pFrom.y;
CLine *pLine = new CLine(pFrom, pTo, nCurWidth, m_crColors[nCurColor]);
m_oaLines.Add(pLine);
You can easily increase this distance to make the drawings more angular. Once you generate the next line segment, you create the line object and add it to the object array. Finally, you set the starting position to the ending position of the line segment you just generated:
pFrom = pTo;
Now you are ready to go through the loop again and generate the next line segment, until you have generated all line segments in this squiggle.
Now that you can generate a single squiggle, the rest of the process is easy. First, you determine how many squiggles will be in the drawing. Next, you loop for the number of squiggles that need to be generated and call the NewLine function once for each squiggle. To add this functionality to your project, add a new member function to the drawing class. Specify the type as void, the declaration as NewDrawing, and the access as public. Edit the function as in Listing 16.3.
LISTING 16.3. THE CModArt NewDrawing FUNCTION.
1: void CModArt::NewDrawing()
}
Displaying the Drawing
To draw the set of squiggles on the drawing area, you can add a function that will loop through the object array, calling the Draw function on each line segment in the array. This function needs to receive the device context as the only argument and must pass it along to each of the line segments. To add this function to your project, add a new member function to the drawing class. Specify the function type as void, the function declaration as Draw(CDC *pDC), and the access as public. Edit the function as in Listing 16.4.
LISTING 16.4. THE CModArt Draw FUNCTION.
1: void CModArt::Draw(CDC *pDC)
Serializing the Drawing
Because you are using the line segment class that you created earlier and have already made serializable, you do not need to add the serialization macros to the drawing class. What you do need to add is a Serialize function that passes the archive object on to the object array, letting the object array and line segment objects do all the serialization work. To add this function to your project, add a new member function to the drawing class. Specify the function type as void, the declaration as Serialize(CArchive &ar), and the access as public. Edit the function as in Listing 16.5.
LISTING 16.5. THE CModArt Serialize FUNCTION.
1: void CModArt::Serialize(CArchive &ar)
Clearing the Drawing
To provide full functionality, you need to be able to delete a drawing from the drawing class so that a new drawing can be created or an existing drawing can be loaded. This is a simple matter of looping through the object array and destroying every line segment object and then resetting the object array. To add this functionality to your project, add a new member function to the drawing class. Specify the type as void, the declaration as ClearDrawing, and the access as public. Edit the function as in Listing 16.6.
LISTING 16.6. THE CModArt ClearDrawing FUNCTION.
1: void CModArt::ClearDrawing()
Completing the Class
Finally, to wrap up your drawing class, you need to initialize the random number generator. The random number generator function, rand, generates a statistically random number sequence based on a series of mathematical calculations. If the number generator starts with the same number each time, then the sequence of numbers is the same each time. To get the random number generator to produce a different sequence of numbers each time your application runs, you need to seed it with a value that is different each time. The typical way to do this is to feed the current system time into the srand function, which seeds the random number generator with a different time each time that the application runs. This seeding of the number generator must be done only once each time the application is run, so you can add this functionality by editing the drawing class constructor with the code in Listing 16.7.
LISTING 16.7. THE CModArt CONSTRUCTOR.
1: CModArt::CModArt()
To complete the class, you need to include all of the necessary header files for the functionality that you've added to this class. The random number generator needs the stdlib.h and time.h header files, and the object array needs the header file for the CLine class. You also need to populate the color table for use when generating squiggles. You can add all of these finishing touches by scrolling to the top of the source code file for the drawing class and adding lines 5, 6, 9, and 12 through 21 in Listing 16.8.
LISTING 16.8. THE CModArt INCLUDES AND COLOR TABLE.
1: // ModArt.cpp: implementation of the CModArt class.
//////////////////////////////////////////////////////////////////////
5: #include <stdlib.h>
6: #include <time.h>
8: #include 'stdafx.h'
9: #include 'Line.h'
10: #include 'ModArt.h'
12: const COLORREF CModArt::m_crColors[8] = ;
You have now completed your library module. Before you go any further, you need to compile your project. Once you compile your project, you cannot run anything because you need to create an application that uses your library module in order to run and test your code. To get ready for creating this test application, close the entire workspace so that you will start with a clean workspace for the test application.
Creating a Test Application
To be able to test your module, you need to create a test application that uses the module. This plain application can contain just enough functionality to thoroughly test the module. All you want to do at this point is test all the functionality in the module; you don't have to create a full-blown application.
When you create your test application, you need to include the header file for the drawing class in the relevant classes in your application. In a typical SDI or MDI application, this means including the header file in the document class at a minimum and probably the view and application class source files also. You also have to add the library file that your module created in the application project so that it will be linked into your appli-cation.
Creating the Test App Shell
Creating a test application shell is a simple matter of creating a standard SDI or MDI application shell. For the purposes of keeping the test application as simple as possible, it's probably advisable to use an SDI application. However, if you've got some functionality in your module that is intended for use in an MDI application, then that application style might be a better selection as your test application.
For the test application for the sample module you created, create a standard SDI application shell using the AppWizard. Give the project a name such as TestApp or some other suitable name. Specify a file extension on the advanced button on the fourth AppWizard step. Otherwise, just go ahead and use the default settings for everything else.
Once you create the application shell, you need to add the library module to the project. You can do this by selecting Project | Add To Project | Files. Once in the Insert Files dialog, specify the file types as library files, as shown in Figure 16.3. Navigate to the debug directory of the module project to find the library module that you created with the previous project. This typically requires moving up one directory level, finding the project directory for the module, and then navigating through it to the debug directory. (If you are building the release version of the module and application, you want to navigate down to the release directory of the module project.) You should be able to find the library file for the module you created, as shown in Figure 16.4. Select this module and click OK to add it to the project.
FIGURE 16.3. Specifying library files.
FIGURE 16.4. Adding a library file to the project.
Once you add the library file to the project, you also need to add the header files for any of the classes in the module that will be used into the appropriate application source code files. For the test application that you are building, this entails adding line 7 in Listing 16.9. You want to add the same line in the include sections of the source code files for the view and application classes as well.
LISTING 16.9. THE CTestAppDoc INCLUDES.
1: // TestAppDoc.cpp : implementation of the CTestAppDoc class
4: #include 'stdafx.h'
5: #include 'TestApp.h'
7: #include '..ModArtModModArt.h'
8: #include 'TestAppDoc.h'
The last thing that you need to do in preparing the application shell is add a variable for any classes from the library module that need to be included in any of the application classes. In the case of the test application that you are building, this is a variable in the document class of the drawing class that you created in the library module project. To add this variable to your application, add a new member variable to the document class. Specify the variable type as the drawing class from the library module (in this instance, CModArt) and specify the name as m_maDrawing and the access as private.
Creating a New Drawing
The first place where you want to put some of the functionality of your module is when you are creating a new document. This is the time to be generating a new drawing. As a result, you want to do two things. First, get the drawing area of the view class, passing it along to the drawing object. Second, tell the drawing object to generate a new drawing. This is all fairly straightforward. To add this functionality to your application, edit the OnNewDocument function in the document class, adding the lines 9-23 in Listing 16.10.
LISTING 16.10. THE CTestAppDoc OnNewDocument FUNCTION.
1: BOOL CTestAppDoc::OnNewDocument()
return TRUE;
Saving and Deleting a Drawing
The other functionality that you want to add to the document class is to save and restore the drawing and to delete the current drawing. These tasks are the last of the document-related functionality of your library module.
To add the functionality to save and restore drawings to your application, edit the Serialize function in the document class. Delete all the current contents of the function, replacing it with a call to the drawing object's Serialize function, as in Listing 16.11.
LISTING 16.11. THE CTestAppDoc Serialize FUNCTION.
1: void CTestAppDoc::Serialize(CArchive& ar)
To add the functionality to delete the current drawing so that a new drawing can be generated or a saved drawing can be loaded, you need to add the event handler for the DeleteContents function to the document class. In this function, you call the drawing object's ClearDrawing function. To add this functionality to your application, use the Class Wizard to add the event handler for the DeleteContents event to the document class. Edit this function, adding line 5 in Listing 16.12.
LISTING 16.12. THE CTestAppDoc DeleteContents FUNCTION.
1: void CTestAppDoc::DeleteContents()
Viewing a Drawing
You need to add one final set of functionality to your test application before you can test your library module: the drawing functionality to the application. This functionality belongs in the view class because it is the object that knows when it needs to redraw itself. Before you can add this functionality to the view class, you need some way for the view class to get access to the drawing object. The easiest way to add this capability is to add another function to the document class that can be called to get a pointer to the drawing object. Once the view has this pointer, it can call the drawing object's own Draw function.
To add the capability to get a pointer to the drawing object to your document class, add a new member function to the document class. Specify the function type as a pointer to the drawing object, in this case, CModArt*, and specify the function declaration as GetDrawing and the access as public. Edit the function, adding the code in Listing 16.13.
LISTING 16.13. THE CTestAppDoc GetDrawing FUNCTION.
1: CModArt* CTestAppDoc::GetDrawing()
Adding the drawing functionality to the view class is a simple matter of editing the OnDraw function in the view class. In this function, you need to get a pointer to the drawing object and then call its Draw function, as in Listing 16.14.
LISTING 16.14. THE CTestAppView OnDraw FUNCTION.
1: void CTestAppView::OnDraw(CDC* pDC)
Once you add all this functionality, you can compile and run your application to test the functionality of your library module. Each time you select File | New from your application menu, a new drawing is created, as in Figure 16.5.
FIGURE 16.5. Creating random squiggle drawings.
Updating the Library Module
Now that you have a working application, let's go back to the library module and make some changes. Whenever you make any changes to the library module code, no matter how minor, you need to relink all applications that use the module in order to get the updates into those applications. This is because the library module is linked into the EXE of the application. It does not remain in a separate file.
To see how this works, reopen the library module project. You will make three changes to this module. First, you'll increase the number of squiggles that may be included in a single drawing. Second, you'll increase the number of line segments that may make up a single squiggle. Third, you'll generate random colors, beyond just the eight colors included in the color table. Once you make these changes, you'll recompile your library module. Once you generate a new module, you'll relink your test application so that you can incorporate these changes into the application.
To make the first change in your module, increasing the number of squiggles that can be in a drawing, edit the NewDrawing function in the drawing class, increasing the modulus value in line 7 of the function, as in Listing 16.15. This will increase the number of possible squiggles in a single drawing from a maximum of 10 to a maximum of 50. There may still be an occasional drawing that doesn't have any squiggles, but you can ignore this possibility for now.
LISTING 16.15. THE MODIFIED CModArt NewDrawing FUNCTION.
1: void CModArt::NewDrawing()
}
With the increased number of squiggles that can be included in a drawing, next you want to increase the number of line segments that may be in a squiggle. To do this, edit the NewLine function and increase the modulus number on line 20 in Listing 16.16 from 100 to 200. While you're in this function, you can also increase the number of colors that may be generated for use in each drawing. First, add three integer variable declarations, one for each of the three additive colors (red, green, and blue, as in lines 9 through 11 in Listing 16.16). Next, generate random values for each of these integers between the values of 0 and 255 (lines 26 through 28). Finally, when creating the CLine object, pass these colors through the RGB function to create the actual color that will be used in the drawing, as in line 41 of Listing 16.16.
LISTING 16.16. THE MODIFIED CModArt NewLine FUNCTION.
1: void CModArt::NewLine()
// Did we run into a memory exception?
catch (CMemoryException* perr)
// Delete the exception object
perr->Delete();
}
// Set the starting point to the end point
pFrom = pTo;
}
}
Now that you've made all the necessary changes to the library module, compile it so that it's ready for use in the test application. If you run your test application from the Start | Run Taskbar option, as in Figure 16.6, you'll notice that there is no noticeable difference in how your application behaves. This is because the application hasn't changed. The application is still using the old version of your library module. To get the test application to use the new version of the library module, reopen the test application project in Visual C++. Build the project, which should not do anything other than relink the project, and then run the application. You should see a significant difference in the drawings that your application is now generating, as shown in Figure 16.7.
FIGURE 16.6. Run the test application from the Start menu.
FIGURE 16.7. The updated test application.
Summary
Today you learned about how to approach creating and designing new classes for your applications. You learned the differences between the different types of classes that are available to you through the New Class Wizard in Visual C++. You also learned how you can create a library module with a set of your functionality that you can hand to other programmers for including in their applications. You learned how this module will be linked into the actual applications, thus not requiring a separate file to be distributed along with the applications.
Tomorrow you will learn about a different approach to creating reusable packaged functionality that you can give to other programmers. You will learn how to create DLLs using Visual C++, what the differences are between creating library modules and DLL, and how you need to approach each task.
Q&A
Q Isn't most functionality packaged in DLLs now? Why would I want to create library modules instead of DLLs?
A Yes, the trend toward packaging functionality modules has been to create DLLs instead of library modules for a number of years now. However, there are still instances where library modules are preferable. If you are creating a module that contains proprietary functionality that you do not want to risk exposing to others, but that is needed for any applications that you or another programmer in your company is building, then you would probably want all that functionality packaged in a library module so that it is internal to the application. Using library modules makes it effectively inaccessible to your competition without significant disassembly and reverse engineering efforts.
Q Why does the header file need to be included in the application that is using my library file?
A The application needs to know about the objects that are in the library file. In the sample application, you didn't need to include the header file for the CLine class because the application didn't directly use or reference the CLine class. However, the application did use the drawing object that was in your library module, so it did need to know about that object, how it is defined, and what functions are available for it. If you don't want the other programmers to know all of the internal structure of your classes, then you can create another header file to be distributed with your library module. This header would contain definitions of all of the same classes that are in the library module but would only provide the public functions and variables that the other programmers can actually access.
Workshop
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. The answers to the quiz questions and exercises are provided in Appendix B, 'Answers.'
Quiz
1. When do you want to create a new MFC class?
2. When you make changes to a library file, what do you have to do to the applications that use the library file?
3. What are the different types of classes that you can create?
4. When you package some functionality in a library file, what do you need to give to other programmers who want to use your library module?
5. What are two of the basic principles in object-oriented software design?
Exercise
Separate the CLine class into a different library module from the drawing class so that you have two library modules instead of one. Link them into the test application.
Teach Yourself Visual C++ 6 in 21 Days -- Ch 17 -- Sharing Your Functionality with Other Applications--Creating DLLs
Teach Yourself Visual C++ 6 in 21 Days
Sharing Your Functionality with Other Applications--Creating DLLs
* Why Create DLLs?
* Creating and Using DLLs
* Designing DLLs
* Creating and Using an MFC Extension DLL
* Creating the MFC Extension DLL
* Adapting the Test Application
* Changing the DLL
* Creating and Using a Regular DLL
* Creating the Regular DLL
* Adapting the Test Application
* Summary
* Q&A
* Workshop
* Quiz
* Exercises
Yesterday you learned how you could create a set of functionality that might be useful for multiple applications and how you could package it in a library file that could be linked into those applications. Today you will learn how to do this same thing, only with a much more dynamic package.
Often, a family of applications will have some functionality in common. When you place this shared functionality into DLLs instead of library modules, all the applications can use the same functionality with only a single copy of the functionality distributed in the form of DLLs, instead of duplicating the same functionality in each of the applications. This method saves disk space on any systems where the applications are installed.
Today, you will learn
* About the different types of DLLs that you can create with Visual C++ and how to determine which type best suits your needs.
* How to build two of these types of DLLs and the different approaches for the various DLL types.
* How to use the functionality for both of these types of DLLs in a Visual C++ application.
* How to determine when an application needs to be relinked when you make modifications to a DLL that is used by the application.
Why Create DLLs?
Dynamic link libraries (DLL) were introduced by Microsoft back in the early days of Windows. DLLs are similar to library modules in that they both contain sets of functionality that have been packaged for use by applications. The difference is when the applications link to the library. With a library module (LIB), the application is linked to the functionality in the library during the compile and build process. The functionality contained in the library file becomes part of the application executable file. With a DLL, the application links to the functionality in the library file when the application is run. The library file remains a separate file that is referenced and called by the application.
There are several reasons for creating DLLs instead of library module files. First, you can reduce the size of the application executable files by placing functionality that is used by multiple applications into DLLs that are shared by all of the applications. You can update and modify functionality in the DLLs without having to update the application executable (assuming that the exported interface for the DLL doesn't change). Finally, you can use DLLs with just about any other Windows programming language, which makes your functionality available to a wider number of programmers, not just fellow Visual C++ programmers.
Creating and Using DLLs
DLLs are library files with compiled code that can be used by other applications. The DLLs expose certain functions and classes to these applications by exporting the function. When a function is exported, it is added to a table that is included in the DLL. This table lists the location of all exported functions contained in the DLL, and it is used to locate and call each of these functions. Any functions that are not exported are not added to this table, and they cannot be seen or called by any outside application or DLL.
An application can call the functions in the DLL in two ways. The more involved method of calling these functions is to look up the location of the desired function in the DLL and get a pointer to this function. The pointer can then be used to call the function.
The other, much easier way (and the only way that you'll use in any of the examples in this book) is to link the application with the LIB file that is created with the DLL. This LIB file is treated by the linker as a standard library file, just like the one that you cre-ated yesterday. However, this LIB file contains stubs for each of the exported functions in the DLL. A stub is a pseudo-function that has the same name and argument list as the real function. In the interior of the function stub is a small amount of code that calls the real function in the DLL, passing all of the arguments that were passed to the stub. This allows you to treat the functions in the DLL as if they were part of the application code and not as a separate file.
NOTE: The LIB file for a DLL is automatically created for the DLL during the compiling of the DLL. There is nothing extra that you need to do to create it.
TIP: Not only is it easier to create your applications using the LIB files for any DLLs that you will be using, but also it can be safer when running the application. When you use the LIB files, any DLLs that are used by your application are loaded into memory the moment the application is started. If any of the DLLs are missing, the user is automatically informed of the problem by Windows, and your application does not run. If you don't use the LIB files, then you are responsible for loading the DLL into memory and handling any errors that occur if the DLL cannot be found.
There are two types of DLLs that you can easily create using Visual C++. These two types are MFC extension DLLs and regular DLLs.
NOTE: You can create other types of DLLs using Visual C++. All these other types of DLLs involve a significant amount of ActiveX functionality, so they are beyond the scope of this book. If you need to build ActiveX in-process server DLLs, or other types of ActiveX DLLs, I recommend that you find an advanced book on Visual C++ that provides significant coverage for these topics.
MFC Extension DLLs
MFC DLLs are the easiest to code and create because you can treat them just like any other collection of classes. For any classes that you want to export from the DLL, the only thing that you need to add is the AFX_EXT_CLASS macro in the class declaration, as follows:
class AFX_EXT_CLASS CMyClass
This macro exports the class, making it accessible to Visual C++ applications. You need to include this macro in the header file that is used by the applications that will use the DLL, where it will import the class from the DLL so that it can be used.
The one drawback to creating MFC extension DLLs is that they cannot be used by any other programming languages. They can be used with other C++ compilers as long as the compiler supports MFC (such as with Borland's and Symantec's C++ compilers).
Regular DLLs
The other type of DLL is a regular DLL. This type of DLL exports standard functions from the DLL, not C++ classes. As a result, this type of DLL can require a little more thought and planning than an MFC extension DLL. Once inside the DLL, you can use classes all you want, but you must provide straight function calls to the external applications.
To export a function, declare it as an export function by preceding the function name with
extern 'C' <function type> PASCAL EXPORT <function declaration>
Include all this additional stuff in both the header file function prototype and the actual source code. The extern 'C' portion declares that this is a standard C function call so that the C++ name mangler does not mangle the function name. PASCAL tells the compiler that all function arguments are to be passed in PASCAL order, which places the arguments on the stack in the reverse order from how they are normally placed. Finally, EXPORT tells the compiler that this function is to be exported from the DLL and can be called outside the DLL.
The other thing that you need to do to export the functions from your DLL is to add all the exported function names to the DEF file for the DLL project. This file is used to build the stub LIB file and the export table in the DLL. It contains the name of the DLL, or library, a brief description of the DLL, and the names of all functions that are to be exported. This file has to follow a specific format, so you should not modify the default DEF file that is automatically created by the DLL Wizard other than to add exported function names. A typical DEF file follows:
LIBRARY 'mydll'
DESCRIPTION `mydll Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
MyFunc1
MyFunc2
If you are using MFC classes in your regular DLLs, you need to call the AFX_MANAGE_STATE macro as the first line of code in all exported functions. This is necessary to make the exported functions threadsafe, which allows your class functions to be called simultaneously by two or more programs (or threads). The AFX_MANAGE_STATE macro takes a single argument, a pointer to a AFX_MODULE_STATE structure, which can be retrieved by calling the AfxGetStaticModuleState function. A typical exported function that uses MFC looks like the following:
extern 'C' void PASCAL EXPORT MyFunc()
Designing DLLs
When you are designing your DLLs, you should be aware that any of the functions in your DLLs can be called simultaneously by multiple applications all running at the same time. As a result, all the functionality in any DLLs that you create must be threadsafe.
All variables that hold any values beyond each individual function call must be held and maintained by the application and not the DLL. Any application variables that must be manipulated by the DLL must be passed in to the DLL as one of the function arguments. Any global variables that are manipulated within the DLL may be swapped with variables from other application processes while the function is running, leading to unpredictable results.
Creating and Using an MFC Extension DLL
To see how easy it is to create and use an MFC extension DLL, you'll convert the library module that you created yesterday into an MFC extension DLL today. After you see how easy it is, and what types of changes you have to make to use the DLL, you'll then reimplement the same functionality as a regular DLL so that you can get an understanding of the different approaches that are necessary with the two DLL styles.
Creating the MFC Extension DLL
To convert the library module you created yesterday into an MFC extension DLL, you need to create a new MFC DLL Wizard project, specifying that the project is an MFC extension DLL. Copy the source code and header files for the line and drawing classes into the project directory. Load the files for the line and drawing classes into the current project. Add the AFX_EXT_CLASS macro to the drawing class. Finally, move the color table from a global static table to a local variable inside the function that creates the squiggles.
To create this DLL, start a new project. Give the project a suitable name, such as ModArtDll, and specify that the project is an MFC AppWizard (DLL) project, as in Figure 17.1. Once in the DLL Wizard, specify that the DLL is an MFC Extension DLL, as in Figure 17.2.
FIGURE 17.1. Selecting the MFC DLL Wizard.
Once you create the DLL shell, open the file explorer and copy the source code and header files for the line and drawing classes (line.cpp, line.h, ModArt.cpp, and ModArt.h) from the library module project you created yesterday into the project directory that you just created. Add all four of these files to the project. Both classes should appear in the Class View of the workspace pane.
FIGURE 17.2. Specifying the DLL type.
Open the header file containing the definition of the drawing class. Add the AFX_EXT_CLASS macro to the class declaration as shown in Listing 17.1. Remove the color table variable from the class declaration also.
LISTING 17.1. THE MODIFIED CModArt CLASS DECLARATION.
1: class AFX_EXT_CLASS CModArt : public CObject
2: ;
You cannot have public static tables in DLLs, so you cannot declare the color table as a public, static member of the drawing class, as it was yesterday. As a result, you'll move it to a local variable in the NewLine member function. Edit the NewLine function to add this local variable and to reset the function to behave as it did in its initial incarnation, as in Listing 17.2.
LISTING 17.2. THE CModArt NewLine FUNCTION.
1: void CModArt::NewLine()
2: ;
// Determine the number of parts to this squiggle
lNumLines = rand() % 100;
// Are there any parts to this squiggle?
if (lNumLines > 0)
// Did we run into a memory exception?
catch (CMemoryException* perr)
// Delete the exception object
perr->Delete();
}
// Set the starting point to the end point
pFrom = pTo;
}
}
After making these changes to the drawing class, you are ready to compile your DLL. Once you compile the DLL, switch over to the file explorer, find the DLL in the debug subdirectory under the project directory, and copy the DLL to the debug directory in the test application project directory.
Adapting the Test Application
To adapt the test application to use the DLL, open the test application project that you created yesterday. You are going to delete the library module that you created yesterday and add the LIB file that was created with the DLL. You are also going to change the header file that is included for the drawing class. After making these two changes, your test application will be ready to use with the DLL.
To delete the library module from the project, open the File View in the workspace pane. Select the LIB file from the list of project files and press the Delete key. Once you delete the library file from the project, select Project | Add To Project | Files from the main menu. Specify the Library Files (.lib) file type, and then navigate to the debug directory of the DLL project. Select the LIB file that was created with your DLL, in this case, ModArtDll.lib. Click OK to add the file to the project.
Once you add the DLL's LIB file, edit the source-code files for the document, view, and application classes, changing the include of the drawing class to point to the project directory of the DLL, as in line 7 in Listing 17.3.
LISTING 17.3. THE CTestAppDoc INCLUDES.
1: // TestAppDoc.cpp : implementation of the CTestAppDoc class
4: #include 'stdafx.h'
5: #include 'TestApp.h'
7: #include '..ModArtDllModArt.h'
8: #include 'TestAppDoc.h'
After making this change to all three source-code files, you are ready to compile and run your test application. You should find your test application running just like it did yesterday, only generating shorter squiggles and using only the eight colors in the color table.
Changing the DLL
Now that you have the test application running with the DLL, you'll make the same changes to the DLL that you made to the library module yesterday. You'll increase the number of squiggles that can be included in a drawing, increase the possible length of each squiggle, and generate any number of colors for use in the squiggles.
To make these changes, switch back to the DLL project. Increase the number of lines that may be generated in the NewDrawing member function of the drawing class. Increase the possible length of the squiggles in the NewLine member function, and add the random colors back in, as in Listing 17.4.
LISTING 17.4. THE MODIFIED CModArt NewLine FUNCTION.
1: void CModArt::NewLine()
2: ;
// Determine the number of parts to this squiggle
lNumLines = rand() % 200;
// Are there any parts to this squiggle?
if (lNumLines > 0)
34:
// Did we run into a memory exception?
catch (CMemoryException* perr)
// Delete the exception object
perr->Delete();
}
// Set the starting point to the end point
pFrom = pTo;
}
}
After making these changes, compile the DLL again. Once you compile the DLL, switch to the file explorer and copy the DLL into the debug directory of the test application again. Once you copy the DLL, run the test application from the Start | Run Taskbar, as in Figure 17.3. You should find that the application has been updated, and it is now including more squiggles and using many different colors.
FIGURE 17.3. Starting the sample application.
Creating and Using a Regular DLL
You might think that you broke the rules about using variables that are not owned by the application in a DLL when you created and used the MFC extension DLL. Well, you didn't. The instance of the drawing class was a member of the document class in the test application. It was created and maintained by the application, not the DLL. Now that you are turning your attention to implementing the same functionality as a regular DLL, this will become clearer.
To convert the MFC extension DLL into a regular DLL, you'll have to convert the drawing class into a series of regular function calls. In the course of making this conversion, the object array must become a member variable of the application document class and must be passed as an argument to every exported function in the DLL.
Creating the Regular DLL
To convert the MFC extension DLL into a regular DLL, you have to start a new project. Visual C++ has to build a project that tells the compiler what type of file it's creating. You can create this new project using the same steps you used to create the MFC extension DLL project, but specify on the DLL Wizard that you are creating a regular DLL. (You can leave the wizard at the default settings.) Once you create the project, you can copy the line and drawing class source code and header files into the project directory and add these files to the project. Once you add these files to the project, you need to begin the process of converting the drawing class into a series of straight function calls.
Altering the Header File
To start with, you need to radically alter the header file for the drawing class so that it will work for a regular DLL. You have to eliminate every trace of the actual class from the header file, leaving only the function calls. All of these functions must be passed in any objects that they need to work with. (Every function will need to be passed the object array as one of its arguments.) Next, you need to slightly modify all the function names so that the compiler does not get mixed up and call a member function of any class by mistake (such as the Serialize function). Finally, each of the public functions must be declared as an exportable function. Making these changes to the header file, you end up replacing the entire class declaration with the function prototypes in Listing 17.5.
LISTING 17.5. THE MODIFIED ModArt HEADER FILE.
1: extern 'C' void PASCAL EXPORT ModArtNewDrawing(CRect pRect,
ACObArray *poaLines);
2: extern 'C' void PASCAL EXPORT ModArtSerialize(CArchive &ar,
ACObArray *poaLines);
3: extern 'C' void PASCAL EXPORT ModArtDraw(CDC *pDC, CObArray *poaLines);
4: extern 'C' void PASCAL EXPORT ModArtClearDrawing(CObArray *poaLines);
5: void NewLine(CRect pRect, CObArray *poaLines);
NOTE: Notice that the object array is always passed as a pointer to each of these functions. Because these functions are adding and removing objects from the array, they need to work with the actual array and not a copy of it.
Adapting the Drawing Generation Functions
Moving to the source-code file, you need to make numerous small yet significant changes to these functions. Starting with the NewDrawing function, you need to pass in the CRect object to get the drawing area. You dropped the function for setting the drawing area because you have no local variables in which you can hold this object. As a result, you are better off passing it to the drawing generation functions. The other change is where you pass in the object array as another argument to the function. You aren't doing anything with either of these arguments in this function, just passing them along to the squiggle generating function. The other alteration in this function is the addition of the AFX_MANAGE_STATE macro as the first line in the body of the function. After making these changes, the NewDrawing function will look like the one in Listing 17.6.
LISTING 17.6. THE ModArtNewDrawing FUNCTION.
1: extern 'C' void PASCAL EXPORT ModArtNewDrawing(CRect pRect,
ACObArray *poaLines)
}
Another change that is required in the NewDrawing function is the addition of the random number generator seeding on line 9. Because there is no class constructor any more, you cannot seed the random number generator in it. Therefore, the next logical place to add this is in the NewDrawing function before any random numbers are generated.
On the NewLine function, the changes are more extensive. First, the CRect object and the object array are passed in as arguments. Second, because this is not an exported function, you do not need to add the AFX_MANAGE_STATE macro. Third, all the places where the CRect member variable is used must be changed to use the CRect that is passed as an argument to the function. Finally, when adding objects to the object array, you need to change this to use the object array pointer that was passed as an argument. Making these changes leaves you with the code in Listing 17.7.
LISTING 17.7. THE NewLine FUNCTION.
1: void NewLine(CRect pRect, CObArray *poaLines)
2: ;
// Determine the number of parts to this squiggle
lNumLines = rand() % 200;
// Are there any parts to this squiggle?
if (lNumLines > 0)
// Did we run into a memory exception?
catch (CMemoryException* perr)
// Delete the exception object
perr->Delete();
}
// Set the starting point to the end point
pFrom = pTo;
}
}
Adapting the Other Functions
Making the necessary changes to the other functions is less involved than the changes to the drawing generation functions. With the rest of the functions, you must add a pointer to the object array as a function argument and then alter the uses of the array to use the pointer instead of the no longer existing member variable. You also need to add the AFX_MANAGE_STATE macro as the first line in each of the remaining functions. This leaves you with the functions shown in Listings 17.8, 17.9, and 17.10.
LISTING 17.8. THE ModArtDraw FUNCTION.
1: extern 'C' void PASCAL EXPORT ModArtDraw(CDC *pDC, CObArray *poaLines)
LISTING 17.9. THE ModArtSerialize FUNCTION.
1: extern 'C' void PASCAL EXPORT ModArtSerialize(CArchive &ar,
ACObArray *poaLines)
Listing 17.10. The MODARTCLEARDRAWING function.
1: extern 'C' void PASCAL EXPORT ModArtClearDrawing(CObArray *poaLines)
Once you make the changes to these functions, the only thing remaining is to remove all code for the class constructor and destructor, along with the code for the SetRect function.
Building the Module Definition File
Before you compile the DLL, you need to add all the function names to the module definition file. You can find this file in the list of source-code files in the File View of the workspace pane. When you open this file, you'll find that it briefly describes the module that you are building in generic terms. You'll see a place at the bottom of the file where you can add the exports for the DLL. Edit this file, adding the exportable function names, as in Listing 17.11.
LISTING 17.11. THE DLL MODULE DEFINITION FILE.
1: ; ModArtRDll.def : Declares the module parameters for the DLL
3: LIBRARY 'ModArtRDll'
4: DESCRIPTION `ModArtRDll Windows Dynamic Link Library'
6: EXPORTS
; Explicit exports can go here
ModArtNewDrawing
ModArtSerialize
ModArtDraw
ModArtClearDrawing
You are now ready to compile your regular DLL. Once you compile the DLL, copy it into the debug directory of the test application.
Adapting the Test Application
To adapt the test application to use the new DLL that you have just created, you need to make a number of changes. First, you need to change the member variable of the document class from an instance of the drawing class to the object array. Next, you need to change the include in the document and view source code to include the header from the new DLL instead of the header from the old DLL. (You can completely remove the include in the application source-code file.) Drop the DLL LIB file and add the LIB file for the new DLL to the project. Change all of the drawing class function calls to call functions in the new DLL instead. Finally, change the GetDrawing function in the document class so that it returns a pointer to the object array, instead of the drawing object.
You can start making these changes by deleting the LIB file from the test application project. Once you delete the file, add the LIB file for the new DLL to the project by selecting Project | Add To Project | Files from the main menu.
Once you switch the LIB files in the project, edit the source code for the document and view classes to change the include statement, changing the project directory to the new DLL project directory. You can edit the application class source-code file and remove the include from this file. Because you are not creating any instances of the drawing class, the application file doesn't need to know about anything in the DLL.
Once you make all those changes, open the header file for the document class. Edit the document class declaration: Change the function type of the GetDrawing function to return a pointer to an object array, remove the drawing class variable, and add an object array variable, as in Listing 17.12. Make only these three changes; do not change anything else in the class declaration.
LISTING 17.12. THE CTestAppDoc CLASS DECLARATION.
1: class CTestAppDoc : public CDocument
2: ;
Modifying the Document Functions
Now that you've made the general changes to the test application, it's time to start making the functionality changes. All the calls to a class method of the drawing object must be changed to the appropriate function call in the new DLL.
The changes necessary in the OnNewDocument function consist of dropping the function call to pass the CRect to the drawing object and replacing the NewDocument function call with the new DLL function--in this instance, ModArtNewDrawing, as shown in line 19 in Listing 17.13.
LISTING 17.13. THE CTestAppDoc OnNewDocument FUNCTION.
1: BOOL CTestAppDoc::OnNewDocument()
return TRUE;
In the Serialize function, change the drawing object Serialize function call to the new DLL serialization function--in this case, ModArtSerialize, as in Listing 17.14.
LISTING 17.14. THE CTestAppDoc Serialize FUNCTION.
1: void CTestAppDoc::Serialize(CArchive& ar)
For the DeleteContents function, you need to change the call to the ClearDrawing function to the new DLL function, ModArtClearDrawing, as in line 5 of Listing 17.15.
LISTING 17.15. THE CTestAppDoc DeleteContents FUNCTION.
1: void CTestAppDoc::DeleteContents()
Finally, for the GetDrawing function, you need to change the function declaration to designate that it's returning a pointer to an object array, just as you did in the header file. Next, you need to change the variable that is being returned to the object array variable that you added to the header file, as in Listing 17.16.
LISTING 17.16. THE CTestAppDoc GetDrawing FUNCTION.
1: CObArray* CTestAppDoc::GetDrawing()
Modifying the View Functions
Switching to the view class, there's only one simple change to make to the OnDraw function. In this function, you need to change the type of pointer retrieved from the GetDrawing function from a drawing object to an object array object, as in line 9 of Listing 17.17. Next, call the DLL function, ModArtDraw, to perform the drawing on the window, as shown in line 11.
LISTING 17.17. THE CTestAppView OnDraw FUNCTION.
1: void CTestAppView::OnDraw(CDC* pDC)
After making all these changes to the test application, you are ready to compile and test it. You should find that the application is working just as it did with the previous DLL. You can also play around with it, going back and changing the DLL, copying the new DLL into the debug directory for the test application, and seeing how the changes are reflected in the behavior of the test application.
CAUTION: The particular example of a regular DLL that you developed in this exercise is still not usable by other programming languages. The reason is that you are passing MFC classes as the arguments for each of the DLL's functions. This still limits the usage to other applications that are built using MFC. To make this DLL truly portable, you need to pass the bare-bones structures instead of the classes (such as the RECT structure instead of the CRect class) and then convert the structures to the classes inside the DLL.
Summary
Today you learned about two more ways that you can package your functionality for other programmers. You learned how you can easily package your classes as an MFC extension DLL and how easily it can be used by a Visual C++ application. You saw how you can make changes to the DLL without having to recompile the applications that use it. You also learned what's involved in creating a regular DLL that can be used with other, non-Visual C++ applications. You saw how you needed to convert the exported classes from the DLL into standard C-style functions and what's involved in adapting an application to use this style of DLL.
Q&A
Q How can I convert the regular DLL so that it can be used by non-Visual C++ applications?
A First, you have to make all the arguments to the functions use the bare-bones structures, instead of the MFC classes. For instance, to convert the ModArtNewDrawing function, change it to receive the RECT structure instead of the CRect class and also to receive a generic pointer instead of a pointer to an object array. You have to make the conversions to the appropriate classes in the DLL, as in lines 4 through 9 in Listing 17.18.
LISTING 17.18. THE ModArtNewDrawing FUNCTION.
1: extern 'C' void PASCAL EXPORT ModArtNewDrawing(RECT spRect,
ALPVOID lpoaLines)
}
You also have to add functions to create and destroy the object array, with the application storing the object array as a generic pointer as in Listing 17.19.
LISTING 17.19. THE ModArtInit FUNCTION.
1: extern 'C' LPVOID PASCAL EXPORT ModArtInit()
Q When do I need to recompile the applications that use my DLLs?
A Whenever you change any of the exported function calls. Changing, adding, or removing arguments to any of these functions would mean recompiling the applications that use the DLL. If you are working with an MFC extension DLL, the applications that use the DLL need to be recompiled if the public interface for the exported classes change or a new function or variable is added or removed. It doesn't matter if the application isn't using any of the functions that were changed; it's still good practice to recompile the applications, just to be sure.
Workshop
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. The answers to the quiz questions and exercises are provided in Appendix B, 'Answers.'
Quiz
1. What kind of DLL do you have to create to make classes in the DLL available to applications?
2. What do you have to add to the class to export it from a DLL?
3. What kind of DLL can be used with other programming languages?
4. If you make changes in a DLL, do you have to recompile the applications that use the DLL?
5. What function does the LIB file provide for a DLL?
Exercises
1. Separate the line class into its own MFC extension DLL and use it with the second (regular) DLL.
2. Alter the line class DLL so that it uses a consistent line width for all lines.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 600
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved