Microsoft Visual C++ .NET 2003 Kick Start

Because the MathLibrary project created a static library, a .LIB file, you can use it from other applications that are outside the AllInOne solution. Typically you deploy a library like this as the .LIB file and the associated header file (or files). In order to create the sample applications in this section, you should copy the .LIB and .H files from the AllInOne folder to another location, to more closely resemble the circumstances under which you would use a real class library.

Creating a Release Version of the Library

The instructions so far in this chapter created a debug version (also called a Debug build) of the library, and a debug version of the test harness to go with it. For distribution to other developers, it's better to create a release version. This has many advantages, including

  • Release builds are smaller, because they don't contain debugging symbols. Even in this tiny application, it makes a difference: The debug library is 10KB and the release library is only 6KB.

  • Release builds are faster, because the optimizer is turned off in Debug builds and some blocks of code are not included by the compiler in Debug builds.

  • Release builds only use Release builds of other libraries; and because license agreements often forbid redistributing debug versions of libraries, a Release build might be the only build you can distribute anyway.

These are the most significant differences between the default Debug build and the default Release build. Once you are experienced working with different kinds of builds, you can use the Properties page for the project to tweak your settings.

To build a release version of MathLibrary.lib, follow these steps:

  1. On the toolbar next to the Start button (a blue rightward-pointing arrow) is a drop-down box with Debug selected. Drop it down and select Release.

  2. Bring up the properties of the Harness project as before. Expand the C/C++ folder and select General. In the Configuration drop-down box at the top of the dialog box, select Debug.

  3. From the Debug settings, select the path to the additional include directory that you added and copy it into the Clipboard.

  4. In the Configuration drop-down box, select All Configurations. Paste the path into the Additional Include Directories property. Now both Debug and Release builds will find the header file in the same location.

  5. Expand the Linker folder and choose Input. Again switch to the Debug configuration (if you're prompted to save the changes you have made so far, choose Yes), and copy the Additional Dependencies path.

  6. Switch to the Release configuration, paste in the path, and change Debug to Release so the path reads as follows :

    ../MathLibrary/Release/MathLibrary.lib

  7. Now the linker uses the Release build of the library when it builds a Release build of the harness, and a Debug build of the library when it builds a Debug build of the harness. Click OK to save your changes.

Build and run the application to be sure you made all your changes correctly.

Calling the Library from an Unmanaged C++ Application

To call the library methods from another application, start by creating an application. The sample in this section is an MFC application just to show that class libraries aren't restricted to console applications. Here's how to create the application and start using methods from the library:

  1. Create an MFC application called UseLibrary .

  2. On the MFC Application wizard, click Application Type on the left.

  3. Select the Dialog Based option. Click Finish to accept all the other defaults.

  4. A form appears in the form editor. This is an MFC form, and working with it is similar to working with Windows Forms in Managed C++, as described in Chapter 4, "Building Simple User Interfaces with Windows Forms."

  5. Click the static text with a caption that starts TODO and press delete to remove it from the form.

  6. Drag two edit controls, two static text controls, and a button from the toolbox onto the form.

  7. Use the Properties window to change the caption of the first label to +, the caption of the second label to no text, the ID of the second label to IDC_ANSWER , and the caption of the button to =.

  8. Rearrange the controls and resize the form so that it resembles Figure 5.3. (If you lose the label that has no text on it, use the drop-down box at the top of the Properties window to select the IDC_ANSWER control and you will see it highlighted on the form.)

    Figure 5.3. Building a simple MFC dialog-based application that will use the library.

  9. Right-click the first edit control and choose Add Variable. On the Add Member Variable wizard, change the Category (on the right) to Value and the Variable type to double. Enter num1 for the name and click Finish.

  10. Repeat this process for the second edit control, naming the variable num2 .

  11. Right-click the IDC_ANSWER static control (you might have to use the Properties window to select it first) and choose Add Variable. This time, leave the variable type and category as the defaults, and simply name the variable answer , and then click Finish.

  12. Double-click the = button to add a handler for the click event and open the source code for editing.

At this point, the dialog box has been created, and all the required variables and methods have been added to the associated class. In order to actually code the button handler, the compiler and linker must be told about the library. Copy the header file, Arithmetic.h, and the release version of the library, MathLibrary.lib, to another folder on your computer. In the instructions that follow, that folder is assumed to be C:\MathLibrary .

Just like the Harness project that tested the library, the UseLibrary project has settings that can be changed to ensure the compiler and linker can find the class definition and the executable code. Add the folder C:\MathLibrary to the Additional Include Directories property by right-clicking UseLibrary in Solution Explorer and expanding C/C++ and then selecting General and entering the path. Then expand the Linker folder and select input, and enter the full path to the .LIB file.

When the library was created, it included a reference to the C runtime library (CRT). Microsoft ships many different versions of the CRT with the compiler. The default settings when creating a static library include the single-threaded version of the CRT. This is also the default for console applications, so both Harness and MathLibrary used the same version of the CRT. However the MFC dialog-based application created in this section uses the multi-threaded debug DLL version of the CRT. As a result, all the functions in the CRT are linked in twiceonce from the single-threaded version of the CRT and once from the multi-threaded debug version. This causes linker errors such as this:

LIBC.lib(malloc.obj) : error LNK2005: _malloc already defined in msvcrtd.lib(MSVCR71D.dll) LIBC.lib(crt0dat.obj) : error LNK2005: _exit already defined in msvcrtd.lib(MSVCR71D.dll)

The project will not build at this point. There are several approaches to solving this problem, some better than others:

  • You could change the library to use the multi-threaded debug DLL version of the CRT. (Bring up the properties for the project, expand C/C++, select Code Generation, and change the Runtime Libraries property.) Unfortunately, this would cause linker errors for your console application.

  • You could change the MFC application to use the single-threaded version of the CRT. This doesn't work: MFC requires some constants that are only in the multi-threaded versions.

  • You could build two versions of the library, each using a different version of the CRT. This is hard to maintain.

  • You could instruct the linker to bring in MathLibrary but to ignore the single-threaded CRT that MathLibrary tries to bring in with it. This is the recommended approach.

To instruct the linker to ignore the single-threaded version of the CRT, bring up the properties for the UseLibrary project. Expand the Linker folder and select Input. Click next to Ignore Specific Library and type libc.lib this is the name of the single-threaded release version of the CRT. Now the project builds successfullyand you don't have to change the library.

The file UseLibraryDlg.cpp is where the button click handler has been added. At the top of this file, after the include statements already present, add an include statement:

#include "arithmetic.h"

CRT LIBRARY NAMES

To find the names of each of the versions of the CRT, search for one of them (such as libc.lib) in the online help.

The handler for the button click creates an instance of the Arithmetic class and uses it to add the numbers that were entered in the edit controls. Then it converts the answer to a string and uses that as the new caption for the static text control. The code looks like this:

void CUseLibraryDlg::OnBnClickedButton1() { UpdateData(true); Arithmetic a; double ans = a.Add(num1,num2); CString answerstring; answerstring.Format("%g",ans); answer.SetWindowText(answerstring); }

The call to UpdateData() copies the entries from the actual controls on the dialog box to the member variables you mapped to them with the Add Variable wizard.

Now you have seen how to use a simple class library from an unmanaged C++ console application and an unmanaged C++ MFC dialog-based application. There is no difference in technique when the library is in the same solution as the calling code or in a different solution: You still need to inform the compiler and the linker of the location of the library's header and .LIB files. When you are using the library from an application that uses the multi-threaded version of the CRT, you need to instruct the linker not to bring the single-threaded version in with the library.

Calling the Library from a Managed C++ Application

Using the library from another unmanaged C++ application was straightforward. Using it from a managed C++ application is also straightforward, even though it combines managed and unmanaged code in one application.

Start by creating a managed C++ console application called ManagedUseLibrary . Update the Additional Include Directories, Additional Dependencies, and Ignore Specific Libraries properties just as for the MFC dialog-based application. Here is the code for the main function:

#include "stdafx.h" #using <mscorlib.dll> using namespace System; #include "arithmetic.h" int _tmain() { Arithmetic a; Console::Write(S" 1.2 + 3.2 = "); Console::WriteLine(__box(a.Add(1.2, 3.2))); Console::Write(S" 2.1 * 3 = "); Console::WriteLine(__box(a.Multiply(2.1, 3))); Console::ReadLine(); return 0; }

It's as simple as that to use an unmanaged class library from a managed application. The C++ team at Microsoft call this "It Just Works"and it does! You'll also see It Just Works referred to as IJW.

BOXING UNMANAGED DATA

The use of the __box extension to convert unmanaged data (such as a double ) to managed data was first discussed in Chapter 1, "C++, Visual C++, and Managed C++: What's the Difference?"

Calling the Library from Other Managed Languages

If you've been reading about the Common Language Runtime and how all the managed languages use the same class libraries and share the same capabilities, you might well assume that the library created in this chapter can be used from Visual Basic .NET and Visual C# as easily as from managed C++. However, that's not the case. All the languages share the same libraries, and produce code that is easily compatible. Code written in managed C++ can be used in a Visual Basic .NET application without any difficulty at all. Managed C++ code can use code written in C# as simply as though it were written in C++. Butand this is a huge advantage of C++the other languages cannot access unmanaged code using It Just Works. There are several techniques for calling unmanaged C++ code from Visual Basic .NET or C#:

  • Write a wrapper class in managed C++ that uses IJW to call into the library. This should be the simplest method, but it has a few pitfalls. Chapter 6, "Writing a Class Library in Managed C++," guides you through them.

  • Convert the unmanaged code into a Dynamic Link Library (DLL) and access it through PInvoke . Chapter 7, "Building DLLs in Unmanaged C++," explains how this is done.

  • Convert the unmanaged code into a COM component and access it through COM Interop. Chapter 8, "Writing COM Components in C++," covers this technique.

Категории