Skip to main content

Beginning Visual C++ 6

Beginning Visual C++ 6

Ivor Horton

ISBN: 978-0-764-54388-3

Aug 1998

1224 pages

Select type: Paperback

Product not available for purchase


What is this book about?

Visual C++ 6.0 was released in 1998 as a component of Visual Studio 6.0. For three years, until the launch of Visual Studio .NET to support the .NET Framework, it was Microsoft's premier development product. Now five service packs old, version 6.0 remains the environment of choice for many developers who haven't yet made the move to .NET. If your aim is to learn how to program in C++ on the Windows platform, with all the help offered by the Visual Studio interface, Visual C++ 6.0 remains a sound choice.

What does this book cover?

Beginning Visual C++ 6 can be broken down into four sections. The first is a fast-paced but thorough tutorial to the C++ language, punctuated with interesting and worthwhile example programs. After that, you'll learn about object orientation with C++, and how this relates to Windows programming - the section ends with the design and implementation of a sizable class-based C++ application.

The third part of the book walks the reader through creating Windows applications using the Microsoft Foundation Classes (MFC). This includes the following:

  • Outputting to the screen and printer
  • Creating menus, toolbars, and dialogs
  • Debugging your program
  • Responding to a user's actions

To illustrate the theory, this section also includes the complete implementation of a simple but fully-featured drawing application. The final section comprises a grounding in programmatic database access, an introduction to Microsoft's Component Object Model (COM), and examples of how to create ActiveX controls using both MFC and the Active Template Library (ATL).

This book was voted's C++ Book of the Year in 1998. It contains countless examples for you to follow and experiment with, and there are challenging exercises and model solutions in every chapter.

Who is this book for?
This book is for anyone who wants to learn C++ and Windows programming with Microsoft Visual C++ 6.0. Although progress will be easier if you have some experience of a programming discipline, an adept newcomer will also succeed in taming object-oriented programming and writing real Windows applications.

Chapter 1. Programming with Visual C++.

Chapter 2. Data, Variables and Calculations.

Chapter 3. Decisions and Loops.

Chapter 4. Arrays, Pointers and References.

Chapter 5. Introducing Structure Into Your Programs.

Chapter 6. More About Program Structure.

Chapter 7. A Taste of Old-Fashioned Windows.

Chapter 8. Structuring Your Data Using Classes.

Chapter 9. More on Classes.

Chapter 10. Class Inheritance.

Chapter 11. An Example Using Classes.

Chapter 12. Debugging.

Chapter 13. Understanding Windows Programming.

Chapter 14. Working with Menus and Toolbars.

Chapter 15. Drawing in a Window.

Chapter 16. Creating the Document and Improving the View.

Chapter 17. Working with Dialogs and Controls.

Chapter 18. Storing and Printing the Document.

Chapter 19. Writing Your Own DLLs.

Chapter 20. Connecting to Data Sources.

Chapter 21. Updating Data Sources.

Chapter 22. Understanding OLE Documents.

Chapter 23. ActiveX Controls.

Chapter 24. Using the Active Template Library.

Appendix A. Keywords in Visual C++.

Appendix B. The ASCII Table.

Appendix C. Solutions to Exercises.

Download the example code for the entire book here
Download the "Advanced Process List" case study here - Word version
Download the "Advanced Process List" case study here - HTML version
Download the "Advanced Process List" source code here
ChapterPageDetailsDatePrint Run
21Page 21: TrialRun.cpp
A line is missing from the TrialRun.cpp program shown on page 21. The listing should be as follows:
// TrialRun.cpp
// Our first program
#include <iostream>

using namespace std;

int main()
   // code as shown in book

The missing line is shown in the screenshot on the following page and is used throughout the rest of the book. 'using namespace std;' allows routines from the C++ standard library to be incorporated into the program (in this case 'cout'). The using directive and namespaces are explained on pages 42 and 85 to 88.


83Page 83: Try It Out - The Scope Resolution Operator

The second sentence of the paragraph introducing the 'Try It Out' concludes with an erroneous statement. The sentence should simply read as follows:

However, it's still possible to get at the global variable using the scope resolution operator (::).

Namespaces are discussed in Chapter 2, not Chapter 1. The scope resolution operator is not discussed until Chapter 8.


85Page 85: Namespaces

The second sentence on page 85 should read as follows:

We'll talk further about namespaces, including how to create your own, shortly.


86Page 86: Demonstrating Namespace Names

In exercise 2_08.cpp, the line,

std::cin << value;

should read as follows:

std::cin >> value;


88Page 88 typo

The third sentence on the page should read as follows:

Of course, you would not usually organize a source file in this way deliberately, but it can arise quite naturally with header files that you include into a program.

Thanks to Ryan McLaughlin


137Clarification on use of cin.getline()

Some readers have asked for clarification regarding the use and behavior of cin.getline() as it compares to using the << operator. In particular, a problem can arise when the former is used immediately after the latter.

These two techniques of obtaining input differ in the way they deal with the carriage return that typically terminates text entered by the user. Specifically, << doesn't remove the carriage return from the input stream, but cin.getline() does. A potential consequence of this is that when cin.getline() is called after using <<, it finds the carriage return that's been left in the input buffer and returns immediately, offering the user no opportunity to provide the input you were seeking.

If you need to use cin.getline() after using <<, one option available to you is to call cin.ignore() before doing so, as this will remove the stray carriage return from the input buffer.


145Page 145: Hexadecimal Numbers

In Visual C++ 6, unlike Visual C++ 5, hexadecimal numbers are not prefixed by 0x when they appear on screen.


147Page 147: Terminating a String

appearing between the quotes and terminated with /0
should read
appearing between the quotes and terminated with \0


178Page 178: Missing Bracket

At the bottom of the page, there should be an opening bracket ( between static_cast<int> and 2.0.


193Missing line of code
The code listing on this page is missing a line that was present in the code on p191. The listing should begin:
#include <iostream>
#include <cstdlib>
using namespace std;

217Misleading section on handling memory allocation errors

The section that occupies the majority of this and the following page is misleading. To use the _set_new_handler() function, it's necessary to include the new.h header file in your code, rather than the standard library file new that the book identifies. However, it's also possible to include new, and then to use the standard library function set_new_handler() (note the lack of a leading underscore) instead.


227Page 227: Eliminating Blanks from a String

In the function eatspaces, the return statement is unnecssary.

Enclosing the lines
if(*(str+i) != ' ')
in curly brackets, i.e.
if(*(str+i) != ' ')
will make the code easier to understand, although this is not essential.

234Page 234: Analyzing a Number

In the final line of the figure at the top of page 234, the factor should be 0.001 (not 0.01, as shown).


238Page 238: Extracting a Substring

The penultimate line of the extract() function on page 238 is superfluous - the program will compile and run with or without it. The exit(1); command ends the program, thus rendering the return pstr; line that follows it redundant. (It removes any need to return from the function at this point.)


241Page 241: Exercise 6-1

In the answer to Exercise 6-1, the variable P[i] in the final line of the function should be enclosed in brackets:

return static_cast<int>(p[i]);


272Page 272: A Complete WindowProc() Function

The WINAPI specifier in both the WindowProc() function definition on page 272 and its prototype on page 273 should be replaced with CALLBACK, so that they read as follows:

long CALLBACK WindowProc(...)


308Page 308: const keyword

The function header double volume() should be followed by the keyword const.


310Page 310: Arrays of Class Objects
The constructor definition in Exercise 8_10 should read:
CBox(double lv, double bv = 1.0, double hv = 1.0): m_Length(lv), m_Breadth(bv), m_Height(hv)
   cout << endl << "Constructor called.";
The code shown in the book causes ambiguity over which constructor should be called, giving rise to the compiler errors described on page 295. In the code in the book, both constructors could be used to initialize the data members of the CBox objects in the array boxes. If a default value for lv is not supplied in the first constructor, the compiler knows it cannot use that constructor for the initialization and so uses the default constructor.

312Page 312: Counting Instances
The constructor definition in Exercise 8_11 should read:
CBox(double lv, double bv = 1.0, double hv = 1.0): m_Length(lv), m_Breadth(bv), m_Height(hv)
   cout << endl << "Constructor called.";
The code shown in the book causes ambiguity over which constructor should be called. A more detailed explanation can be found in the erratum for page 310.

320Page 320: Exercises 8-2 and 8-3

The model answers to Exercises 8-2 and 8-3 use destructors, which aren't covered until the next chapter.


330Page 330: DisplayMessage() function definition
​​ If you try to incorporate the code snippets shown on page 330 into Exercise 09_02 the program will not compile. This is because the ShowIt() function is defined as having a void return type but the << operator cannot take a right hand operand of type void. The DisplayMessage() function should, therefore, be defined as follows:
void DisplayMessage(CMessage localMsg)
   cout << endl << "The message is: ";

Thanks to Sasa Bosnjak.

343Page 343: comment
The second comment in the code at the bottom of the page should read //Check addresses. If equal, return a reference to the 1st operand.

343Page 343: motto1
The line
Motto1 = *pMess;
should read
motto1 = *pMess;
where motto1 starts with a lower case 'm'.

347Page 347: Overloading the Addition Operator

The second comment (// Class definition at global scope) wraps on to the next line. "scope" is not a command!


351Page 351: Defining a Class Template
A semicolon is missing from the default constructor at the top of the page. The default constructor should read as follows:
  m_Free = 0;
Also, the comment on the penultimate line of the class listing wraps onto the next line. m_Values is not a command, it is part of the comment on the previous line, which should read as follows:
int m_Free;   // Index of free location in m_Values

363Page 363: Analyzing CBox Objects

At the top of page 363, the return statement includes a static_cast with a bracket instead of an angled bracket. The line should read as follows:

return static_cast<int>(...code as shown in book...);


374Page 374: Adding Global Functions

The second parameter of the CBox multiply operator should be a reference. The next-to-last line in the list of declarations for global box operators should read as follows:

CBox operator*(int n, const CBox& aBox);


374Using Visual C++ Macros

When you follow the book's instructions for running macros, you may find yourself confronted by a blank Macro dialog box. Should this happen to you, there's a relatively easy fix.

With no files open in Visual C++6, you need to click on Tools | Macro. Then, in the Macro dialog box that appears, you have to click on Options | Loaded Files. Finally, in the Add-ins and Macro Files tab of the Customize dialog box that appears, you need to enable the Sample macro by clicking on the check box. After you've closed the dialog, you can follow the book's directions to run the macro.


380Page 380: Summary

In the final line of the first bullet point, "default constructor" should read, "default destructor".


386Page 386: Deriving Classes from a Base Class

The destructor in listing 10_01-02 should not have a semicolon after the closing curly bracket.


391Page 391: Constructor Operation in a Derived Class

The first line of the first paragraph in the section entitled "Constructor Operation in a Derived Class" should read as follows:

Although we said that the base class constructor is not inherited in the derived class, it still exists for the base part of the derived class object.


404Friend Classes
The fourth line on page 404 reads:

friend CCarton;

It should read as follows:

friend class CCarton;

420Page 420: Calling the Wrong Destructor

In the paragraph below the screenshot, the third sentence should read as follows:

For the other objects, the correct destructor calls occur with the derived class destructor being called first, followed by the base class destructor.


443Page 443: Making the CCalculator Class Safe to Use

Due to what appears to be a bug in the ClassWizard, using the Add Member Function dialog to add the assignment operator to the CCalculator class will not work. An implementation will be provided in the Calculator.cpp file but no declaration will be added to the class definition in Calculator.h.

Consequently you shouldn't bother with the Wizard and should add the declaration manually. The declaration should be placed in the private section of the class, as shown on page 445.

As stated in the book, no implementation is needed - if you've used the Wizard to add the assignment operator function you should delete the implementation that the Wizard produces.


444Page 444: Starting the Calculator
The declaration of the copy constructor for the CCalculator class should be in the private section of the class (as stated in the text) and not in the public section (as shown in the class definition).

The correct class definition is shown below:
class CCalculator
    void Run() const;
    virtual ~CCalculator();
    CCalculator(const CCalculator& rCalculator);
    CCalculator& operator=(const CCalculator& rhs);
    CKeyboard* m_pKeyboard;
    CLogicUnit* m_pLogicUnit;
    CDisplay* m_pDisplay;

444Page 444: implementation

In later editions, the sentence,
"If you have used ClassWizard to add the operator function, you will need to delete the implementation as it is added automatically",
is added to the first paragraph on the page.


450Page 450: Declaration of CLogicUnit::OnDigit()

The function CLogicUnit::OnDigit() should be declared as:

void OnDigit(const int& digit);

This amendment to the function's parameter also applies to the declaration of OnDigit() shown on page 452 and its implementation shown on page 453.


451Page 451: Logic Unit Registers Table

Some of the values shown in the table do not accurately reflect the workings of the code.
When the '+' is entered the values held in the registers should be as follows:
Display = 1.5
Multiply = 1.5
Add = 1.5

When '5' is entered the values should be:
Display = 5
Multiply = 1.5
Add = 1.5

When 'Enter' is pressed, the values should be:
Display = 6.5
Multiply = 1.5
Add = 6.5

Thanks to Sasa Bosnjak.


453Page 453: OnDigit function

The OnDigit function should be declared as follows.
void OnDigit(const int& digit);


454Page 454: The CRegister Class

In the CRegister class, the OnDigit() function should be declared as follows:

void OnDigit(const int& digit);


457Page 457: Handling a Digit in CRegister

The parameter of the function CRegister::OnDigit() should be:

const int& digi


458Page 458: Declaration of m_bBeginValue

The CRegister member variable m_bBeginValue should be declared as bool (as stated in the text) rather than int (as shown in the code).


459Page 459: operator+ function

In the comments in the definition of the operator+ function, R1 and R2 should be replaced by reg1 and reg2.


465Page 465: CLogicUnit contructor

The second line of code at the bottom of the page should end m_pCalc(pCalc) with an underscore rather than a hyphen.


470Page 470: Register Contents Table
Some of tde values and actions shown in tde table do not accurately reflect the workings of the code.
tde lower part of tde table should appear as follows:
Key Action m_DisplayReg m_AddReg m_MultiplyReg
+ m_DisplayReg * m_MultiplyReg 3 1 6
  Copy m_MultiplyReg to M_DisplayReg 6 1 6
  m_DisplayReg + m_AddReg 6 7 6
  Copy m_AddReg to m_DisplayReg 7 7 6
4 Store in m_DisplayReg 4 7 6
Enter m_DisplayReg + m_AddReg 4 11 6
  Copy m_AddReg to m_DisplayReg 11 11 6

471Page 471: Completing the CDisplay Class

The incomplete declaration of the CRegister class (which you are told to add) is missing from the contents of the file Display.h. The following line should be placed immediately above the incomplete declaration of CCalculator:

class CRegister;


484Page 484 Missing Semicolon

First 3 lines of the function declaration should read:

int main()
long* pnumber = NULL;
long number1 = 55;
long number2 = 99;

Thanks to Clinton Hess


487Page 487: typing error

On the final line "withhandling" should read "with handling".


512Page 512: Functions Checking the Free Store

In the second sentence of the section entitled Functions Checking the Free Store, ctrdbg.h should read crtdbg.h.


521Page 521: Preprocessor symbol

The preprocessor symbol _NDEBUG should be _DEBUG.


535Page 535: MFC Hierarchy Diagram

The first sentence of the second paragraph on page 535 should read as follows:

Each arrow in the diagram points to a derived class from its base class.


618Page 618 Typing error

The final sentence on the page should read:

. Create a new header file and add the following skeleton code:

Thanks to David Sneed


621Page 621: comment wrap

In the code at the top of the page, a comment, // Get the bounding rectangle for an element, has wrapped to the next line.


662Page 662 Error in code
The code snippet presented on this page should only contain one call to SetROP2 to ensure that curves are drawn with solid rather than dotted lines.
The code should therefore read as:
void CSketcherView::OnMouseMove(UINT nFlags, CPoint point)
  //Define a Device Context object for the view
  CClientDC aDC(this);
  if((nFlags & MK_LBUTTON) & (this == GetCapture()))

Thanks to Peter Schoellkopf

690Page 690: Highlighting Elements

An extra argument has been included in the constructor for the line object. The final argument should not be added until the next chapter, so the constructor declaration should read as follows: CLine(const CPoint& Start, const CPoint& End, const COLORREF& Color);


717Page 717: Adding Pen Widths to the Elements

The constructor declaration in the CLine class should read as follows:CLine(const CPoint& Start, const CPoint& End, const COLORREF& Color, const int& PenWidth);


785Page 785: Writing Your Own DLLs

There are two references to, 'DLLs which don't involve MFC' - a topic which is not covered in the book. The first is in the bulleted list on page 785 and the second is in the 'Writing DLLs' section on page 792.


792Page 792: Writing DLLs

The last sentence in the section entitled Writing DLLs which reads &quotFinally, we will cover how to write plain Windows DLLs which do not involve MFC at all." should be deleted.


882Expediting the Update
Currently the database will not be updated with changed records on exiting the routine. This can be acheived by inserting the following line:
void CDBSampleView::OnEditorder()
  if( m_pSet->CanUpdate() )
      if (m_UpdateMode)
        m_EditCtrl.SetWindowText("Edit Order");
        UpdateData(true);  //Added Line
Thanks to Scott Weber


883Page 883: Controlled Updating
In order to correctly cancel an update operation, an extra line needs to be added to the definition of OnCancel().
The extra line should read:
It should be inserted after the line that cancels the update operation so that the definition of OnCancel() reads as follows:
void COrderDetailsView::OnCancel() 
  m_UpdateMode = !m_UpdateMode;
UpdateData() is explained on page 898.

Thanks to Christopher Hutchinson.

902Page 902: Selecting Products for an Order

​ In the definition of InitializeView(), m_CompanyName should be replaced with m_CustomerName. The fourth line in the body of the function should read:
m_CustomerName = pDoc->m_Order.m_ShipName;


932Page 932: Implementation of OnUpdate()
The implementation of the OnUpdate() member function of CWrxContainerView is missing from Chapter 22. The following text should have been added immediately before the 'Setting the Tracker Style'section:

We need to override the default implementation of the OnUpdate() function in order to make use of the hint information passed in the second and third parameters. Add this function to the CWrxContainerView class using ClassWizard, giving the return type as void and the name as OnUpdate(CView* pSender, LPARAM lHint, CObject *pHint). The implementation is shown below:

void CWrxContainerView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint)
    //We need to pass a rectangle to InvalidateRect() for the item
    //that takes account of the tracker
    CRectTracker tracker;  //The tracker
    SetupTracker((CWrxContainerItem*)pHint, &tracker);  //...setup for the item
    CRect rect;
    tracker.GetTrueRect(rect);  //Get the tracker rectangle
    InvalidateRect(rect);  //and invalidate that.

945Page 945: Drawing the Document
In the OnUpdate() function, pHint is cast to CElement instead of CElement*. The line should, therefore, read as follows:
CRect aRect = static_cast<CElement*>

982Page 982: Testing the Control

In the third paragraph on page 982, the menu option that you should select is:

Edit | Insert New Control

1045Page 1043: Definition of OnDraw()
The contents of the file OurConstants.h should be as shown on pages 975 and 978. The full listing is given below:
//Definition of constants
#ifndef __OURCONSTANTS_H__
#define __OURCONSTANTS_H__

const int STOP          = 101;
const int GO            = 103;
const int READY_TO_STOP = 104;

const COLORREF RED      = RGB(255, 0, 0);
const COLORREF ORANGE   = RGB(200, 100, 0);
const COLORREF GREEN    = RGB(0, 255, 0);
const COLORREF GRAY     = RGB(100, 100, 100);


1051Page 1051: Adding the Signal to the Control

The CSignal constructor should include the following line (which is added by the ATL COM AppWizard):

m_bWindowOnly = TRUE;

1051Page 1051: Drawing the Control

In the OnDraw function definition at the bottom of the page, the first line should read


Inthe same function definition, the static_cast is inappropriate. Therefore the first line in the body of the function should read

RECT& rc = *(RECT*)di.prcBounds; // Get control rectangle


1053Page 1053: Definition of StartSignal()

In the definition of StartSignal(), the call to SetSignalState() should be a call to SetState(). The second line in the body of the function should, therefore, read as follows:

m_TrafficSignal.SetState(m_bStartRed ? STOP : GO);

1054Page 1054: Starting and Stopping the Signal

In the first paragraph on page 1054, SetTimer() is described as having three arguments. In fact, the version of SetTimer() used here only has two arguments, as shown in the StartSignal() function definition. The two arguments are the timer ID and the time interval in milliseconds.


1062Page 1062: screenshot

The screenshot should reflect the fact that the states are 101, 103, 104 (not 101, 102, 103).


1065Page 1065: Appendix A

The keywords shown below should be included in the list:


The following are not Visual C++ keywords:



1075Page 1075: Answer to exercise 2-4

The answer to exercise 2-4 should have only one of the arguments in the division cast to a double, as described in the text but not as shown in the line of code. The line of code should read as follows:

double aspect = static_cast<double>(width)/height;

The code as shown in the book will produce the answer 1 (when dividing 640 by 480). This is because both width and height are integer variables and therefore the result of (width/height) will be an integer also; the nearest integer to the actual result of 1.333 being 1. Only when the division has been performed is the result cast to a double (i.e. the integer 1 is cast to the double 1.0).
In the correct version only width is cast to a double. Width is then divided by the integer height, producing a result that is a double (in this case 1.333).


1111Page 1111 - Ex14-4
The code solution given for Exercise 14-4 should read as follows:
void CSketcherDoc::OnUpdateColorBlack(CCmdUI* pCmdUI)
  //Set menu item checked if the current color is black
  //Set upper case for a selected item, lower case otherwise
  if(m_Color == BLACK)
Thanks to Vincent Hand

1135Page 1135: Solution to exercise 21-3

When you create the recordset class, CEmployeeSet, based on the employee table, the wizard will create the class with member variables corresponding to all the fields in the table. As you can see from the constructor shown on page 1135, most of these members have been deleted using ClassWizard, leaving only the 3 fields we need. Care should be take when deleting fields from a recordset class not to delete the key field(s).

In the second paragraph on page 1136, the text reads "To control the list box, add another variable to CCustomerView". Note that you should use ClassWizard to add this variable, m_EmployeeCtrl. As well as adding the member variable to the header file (as shown in the code snippet), the wizard will automaically add the following line to the DoDataExchange() method in the .cpp file:DDX_Control(pDX, IDC_EMPLOYEENAME, m_EmployeeCtrl);Remember that this function controls the exchange of values between data members and dialog controls.

Note that in the code shown for CCustomerView::OnInitialUpdate() at the bottom of page 1136, a call to SetNewOrderID() is shown. This is superfluous, as the method has already been called from CMainFrame::SelectView().

In the first paragraph on page 1138, the text states "the listbox handler we added to CCustomerView ...". Note that you need to add this handler at this point using ClassWizard.