Cover Copyright© 1998 by David J Kruglinski PUBLISHED BY Microsoft Press A Division of Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyright (c) 1998 by David J Kruglinski All rights reserved No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the publisher Library of Congress Cataloging-in-Publication Data Kruglinski, David Programming Microsoft Visual C++ / David J Kruglinski, Scot Wingo, Shepherd 5th ed p cm Rev ed of: Inside Visual C++ Includes index ISBN 1-57231-857-0 1 C++ (Computer program language) Microsoft Visual C++ I Wingo, Scot II Shepherd, George, 1962- III Kruglinski, David Inside Visual C++ IV Title QA76.73.C153K78 1998 005.13'3 dc21 98-27329 CIP Printed and bound in the United States of America 1 2 3 4 5 6 7 8 9 WCWC 3 2 1 0 9 8 Distributed in Canada by Penguin Books Canada Limited A CIP catalogue record for this book is available from the British Library Microsoft Press books are available through booksellers and distributors worldwide For further information about international editions, contact your local Microsoft Corporation office or contact Microsoft Press International directly at fax (425) 936-7329 Visit our Web site at mspress.microsoft.com Macintosh and TrueType fonts are registered trademarks of Apple Computer, Inc Intel is a registered trademark of Intel Corporation ActiveX, FoxPro, FrontPage, Microsoft, Microsoft Press, MS, MS-DOS, Outlook, PowerPoint, SourceSafe, Visual Basic, Visual C++, Visual J++, Win32, Win64, Windows, and Windows NT are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries Other product and company names mentioned herein may be the trademarks of their respective owners Acquisitions Editor: Eric Stroo Project Editor: Rebecca McKay Technical Editor: Jean Ross Acknowledgments I first met David Kruglinski at a software development conference I had just quit my job and started a new company, and I approached David at an author signing for an earlier edition of this book Our new company was going to focus on Microsoft Foundation Class (MFC)/Microsoft Visual C++ class libraries I hoped that David would listen to our ideas and maybe provide some feedback on our products and their usefulness to the Visual C++ development community—a community that he had both helped to build and understood like the back of his own hand Much to my surprise, David was very excited about the product ideas and asked if I could send him evaluation copies of our products I did, and we started a long e-mail conversation about our products and ideas for improvements David gave his time, expertise, and great opinions freely, without ever asking for any compensation—he genuinely wanted to help us make products for the Visual C++ developer that would make MFC/Visual C++ better I first heard about David's fatal paragliding accident via a posting on a newsgroup and initially thought it was some kind of cruel hoax I called David's agent, who checked into the story, and much to my shock it was true With David's passing, the Visual C++ community has lost one of its brightest and most giving stars Talk to any Visual C++ developers about how they first learned Visual C++ and invariably they will say, "the Kruglinski book!" The Visual C++ community owes David greatly for what he gave us and taught us about Visual C++ over the years It goes without saying that he should receive special acknowledgment for this book, and our thoughts go out to his family and friends for their loss It is a great honor to carry on the Kruglinski tradition with this fifth edition of Inside Visual C++ (now called Programming Microsoft Visual C++, Fifth Edition) We have done our best to stay true to David's vision of this book, following his format and style as closely as possible Thanks to my wife Kris and to my son, Sean, for putting up with all of the late nights and weekends of writing Many thanks also go to my coauthor, George Shepherd, who always helps me get motivated for a late night of book writing with his upbeat, wacky, and great personality Thanks to Claire Horne, our agent, for helping us get on board with the project Visual C++, and therefore this book, wouldn't exist if not for the many members of the Visual C++ team Special thanks to Mike Blaszczak, Walter Sullivan, Dean McCrory, Rick Laplante, Marie Huwe, Christian Gross, and Jim Springfield for all of the help they have provided over the years Finally, but not least, thanks to the folks at Microsoft Press who worked on this project—especially Kathleen Atkins, Jim Fuchs, Becka McKay, John Pierce, Jean Ross, Eric Stroo, and the entire production team who worked extremely hard to get this large book out and into your hands with the highest quality possible —Scot Wingo Much work goes into writing books—even revisions of existing work I'd like to acknowledge the following people for helping me get this work out the door: First I'd like to thank my wife Sandy for sticking with me while I worked to get the pages and chapters out Sandy has been an invaluable source of encouragement throughout my software endeavors Thanks to my son, Teddy, for being patient with me as I bowed out on various activities every once in a while I wish to thank my mother Betsy for engendering in me a desire to write and my twin brother Patrick for being a great Java foil for me—and for arguing cogently with me about various hardware and software platform issues Thanks to Claire Horne of the Moore Literary Agency for helping to get this project rolling with Microsoft Press To Scot Wingo, thanks for taking on another writing project with me And thanks to the folks at Stingray—you all are a great bunch to work with Thanks to all the folks at DevelopMentor for providing a great training and learning environment And thanks to Don Box for continuing to explain COM in a way that makes sense Getting a book out involves more than just authors I wish to thank everyone at Microsoft Press who helped kick Programming Microsoft Visual C++, Fifth Edition out the door, especially Eric Stroo for his kindness and patience, Kathleen Atkins and Becka McKay for sifting through our text and making it ever more readable, Jean Ross for balancing out the technical review to catch even the most minute error, and John Pierce for keeping everything on track Finally, thanks to David Kruglinski for starting this project While I never had the opportunity to meet David, his writing had a measuable impact on me when first learning MFC I hope Scot and I did justice to the work you began —George Shepherd Introduction The 6.0 release of Visual C++ shows Microsoft's continued focus on Internet technologies and COM, which are key components of the new Windows Distributed interNet Application Architecture (DNA) In addition to supporting these platform initiatives, Visual C++ 6.0 also adds an amazing number of productivity-boosting features such as Edit And Continue, IntelliSense, AutoComplete, and code tips These features take Visual C++ to a new level We have tried to make sure that this book keeps you up to speed on the latest technologies being introduced into Visual C++ MFC, ATL, and WFC—Is MFC Dead? Ever since Microsoft released the Active Template Library (ATL) as part of Visual C++, Windows developers have speculated that the Microsoft Foundation Class Library (MFC) was no longer "en vogue" at Microsoft and that future efforts would focus on newer libraries such as ATL Recently, Microsoft released another class library, Windows Foundation Classes (WFC), for Java Windows developers, which has unfortunately helped to fan the rumors that "MFC is dead." The rumors of MFC's demise are definitely premature Visual C++ 6.0 has added significant functionality to MFC and ATL in parallel, which indicates that both libraries will receive equal attention moving forward Part of the problem is that the design goals of each library are sometimes not clearly stated and therefore are not clearly understood by the Visual C++ developer MFC is designed to be a great class library for creating graphically rich, sophisticated Windows applications ATL is designed to make it easy to create extremely lightweight COM objects and ActiveX controls Each of these design goals has resulted in a different library to empower the developer Another common misconception is that MFC and ATL are mutually exclusive This is definitely not the case! In fact, it is very easy to create ATL-based COM objects that use MFC The only issue is that since many developers choose ATL for its lightweight nature, using MFC, which is feature-rich and "heavy," seems to contradict the reason for choosing ATL While this might be the case for some developers, it doesn't make ATL and MFC mutually exclusive While ATL does not replace MFC, we do think it is an important part of Visual C++, so in this edition of Programming Microsoft Visual C++ we have added two chapters that cover the ATL class libraries C++ vs Java In the last couple of years, there has been a great deal of interest in the Java programming language Why should you choose C++ over Java? In the first place, a compiled program will always be faster than an interpreted program Think about a high-performance spreadsheet program with cell formulas and macros Now imagine the Java virtual machine interpreting the code that, in turn, interprets the formulas and macros Not pretty, is it? With just-in-time compilation, it's necessary to compile the program every time you load it Will that code be as good as the optimized output from a C++ compiler? Execution speed is one factor; access to the operating system is another For security reasons, Java applets can't perform such tasks as writing to disk and accessing serial ports In order to be platform-independent, Java application programs are limited to the "lowest common denominator" of operating system features A C++ program for Microsoft Windows is more flexible because it can call any Win32 function at any time Java will be an important language, but we believe it's just another language, not a revolution If you need an Internet applet or a truly platform-independent application, choose Java If you need efficiency and flexibility, choose C++ Who Can Use This Book The product name "Visual C++" misleads some people They think they've bought a pure visual programming system similar to Microsoft Visual Basic, and for the first few days the illusion persists However, people soon learn that they must actually read and write C++ code The Visual C++ wizards save time and improve accuracy, but programmers must understand the code that the wizards generate and, ultimately, the structure of the MFC library and the inner workings of the Windows operating system Visual C++, with its sophisticated application framework, is for professional programmers, and so is this book We assume that you're proficient in the C language—you can write an if statement without consulting the manual And we assume that you've been exposed to the C++ language—you've at least taken a course or read a book, but maybe you haven't written much code Compare learning C++ to learning the French language You can study French in school, but you won't be able to speak fluently unless you go to France and start talking to people Reading this book is like taking your trip to France! We won't assume, however, that you already know Windows programming We're sure that proficient C programmers can learn Windows the MFC way It's more important to know C++ than it is to know the Win32 application programming interface (API) You should, however, know how to run Windows and Windows-based applications What if you're already experienced with the Win32 API or with the MFC library? There's something in this book for you too First you'll get some help making the transition to Win32 programming Then you'll learn about new features such as Data Access Objects (DAO), ActiveX control container support, and the controls introduced with Windows 95 If you haven't already figured out the Component Object Model (COM), this LPARAM); ON_WM_PARENTNOTIFY_REFLECT() afx_msg void ParentNotify(UINT, LPARAM); ON_WM_QUERYDRAGICON() afx_msg HCURSOR OnQueryDragIcon(); ON_WM_QUERYENDSESSION() afx_msg BOOL OnQueryEndSession(); ON_WM_QUERYNEWPALETTE() afx_msg BOOL OnQueryNewPalette(); ON_WM_QUERYOPEN() afx_msg BOOL OnQueryOpen(); ON_WM_RBUTTONDBLCLK() afx_msg void OnRButtonDblClk(UINT, CPoint); ON_WM_RBUTTONDOWN() afx_msg void OnRButtonDown(UINT, CPoint); ON_WM_RBUTTONUP() afx_msg void OnRButtonUp(UINT, CPoint); ON_WM_RENDERALLFORMATS() afx_msg void OnRenderAllFormats(); ON_WM_RENDERFORMAT() afx_msg void OnRenderFormat(UINT); ON_WM_SETCURSOR() afx_msg BOOL OnSetCursor(CWnd*, UINT, UINT); ON_WM_SETFOCUS() afx_msg void OnSetFocus(CWnd*); ON_WM_SETTINGCHANGE() afx_msg void OnSettingChange(UINT, LPCTSTR); ON_WM_SHOWWINDOW() afx_msg void OnShowWindow(BOOL, UINT); ON_WM_SIZE() afx_msg void OnSize(UINT, int, int); ON_WM_SIZECLIPBOARD() afx_msg void OnSizeClipboard(CWnd*, HGLOBAL); ON_WM_SIZING() afx_msg void OnSizing(UINT, LPRECT); ON_WM_SPOOLERSTATUS() afx_msg void OnSpoolerStatus(UINT, UINT); ON_WM_STYLECHANGED() afx_msg void OnStyleChanged(int, LPSTYLESTRUCT); ON_WM_STYLECHANGING() afx_msg void OnStyleChanging(int, LPSTYLESTRUCT); ON_WM_SYSCHAR() afx_msg void OnSysChar(UINT, UINT, UINT); ON_WM_SYSCOLORCHANGE() afx_msg void OnSysColorChange(); ON_WM_SYSCOMMAND() afx_msg void OnSysCommand(UINT, LPARAM); ON_WM_SYSDEADCHAR() afx_msg void OnSysDeadChar(UINT, UINT, UINT); ON_WM_SYSKEYDOWN() afx_msg void OnSysKeyDown(UINT, UINT, UINT); ON_WM_SYSKEYUP() afx_msg void OnSysKeyUp(UINT, UINT, UINT); ON_WM_TCARD() afx_msg void OnTCard(UINT, DWORD); ON_WM_TIMECHANGE() afx_msg void OnTimeChange(); ON_WM_TIMER() afx_msg void OnTimer(UINT); ON_WM_VKEYTOITEM() afx_msg int OnVKeyToItem(UINT, CListBox*, UINT); ON_WM_VKEYTOITEM_REFLECT() afx_msg int VKeyToItem(UINT, UINT); ON_WM_VSCROLL() afx_msg void OnVScroll(UINT, UINT, CScrollBar*); ON_WM_VSCROLL_REFLECT() afx_msg void VScroll(UINT, UINT); ON_WM_VSCROLLCLIPBOARD() afx_msg void OnVScrollClipboard(CWnd*, UINT, UINT); ON_WM_WINDOWPOSCHANGED() afx_msg void OnWindowPosChanged (WINDOWPOS*); ON_WM_WINDOWPOSCHANGING() afx_msg void OnWindowPosChanging (WINDOWPOS*); ON_WM_WININICHANGE() USER-DEFINED MESSAGE CODES afx_msg void OnWinIniChange(LPCTSTR); Map Entry Function Pro ON_MESSAGE(, ) afx_msg LRES memberFxn(W LPARAM); ON_REGISTERED_MESSAGE (, ) afx_msg LRES memberFxn(W LPARAM); ON_REGISTERED_THREADMESSAGE(, afx_msg void ) memberFxn(W LPARAM); ON_THREAD_MESSAGE (, ) afx_msg void memberFxn(W LPARAM); Appendix B MFC Library Runtime Class Identification and Dynamic Object Creation Long before runtime type information (RTTI) was added to the C++ language specification, the MFC library designers realized that they needed runtime access to an object's class name and to the position of the class in the hierarchy Also, the documentview architecture (and, later, COM class factories) demanded that objects be constructed from a class specified at runtime So the MFC team created an integrated macro-based class identification and dynamic creation system that depends on the universal CObject base class And in spite of the fact that the Visual C++ version 6.0 compiler supports the ANSI RTTI syntax, the MFC library continues to use the original system, which actually has more features This appendix explains how the MFC library implements the class identification and dynamic creation features You'll see how the DECLARE_DYNAMIC, DECLARE_DYNCREATE, and associated macros work, and you'll learn about the RUNTIME_CLASS macro and the CRuntimeClass structure Getting an Object's Class Name at Runtime If you wanted only an object's class name, you'd have an easy time, assuming that all your classes were derived from a common base class, CObject Here's how you'd get the class name: class CObject { public: virtual char* GetClassName() const { return NULL; } }; class CMyClass : public CObject { public: static char s_lpszClassName[]; virtual char* GetClassName() const { return s_lpszC }; char CMyClass::s_szClassName[] = "CMyClass"; Each derived class would override the virtual GetClassName function, which would return a static string You would get an object's actual class name even if you used a CObject pointer to call GetClassName If you needed the class name feature in many classes, you could save yourself some work by writing macros A DECLARE_CLASSNAME macro might insert the static data member and the GetClassName function in the class declaration, and an IMPLEMENT_CLASSNAME macro might define the class name string in the implementation file The MFC CRuntimeClass Structure and the RUNTIME_CLASS Macro In a real MFC program, an instance of the CRuntimeClass structure replaces the static s_lpszClassName data member shown above This structure has data members for the class name and the object size; it also contains a pointer to a special static function, CreateObject, that's supposed to be implemented in the target class Here's a simplified version of CRuntimeClass: struct CRuntimeClass { char m_lpszClassName[21]; int m_nObjectSize; // used for memory validation CObject* (*m_pfnCreateObject)(); CObject* CreateObject(); }; The real MFC CRuntimeClass structure has additional data members and functions that navigate through the class's hierarchy This navigation feature is not supported by the official C++ RTTI implementation This structure supports not only class name retrieval but also dynamic creation Each class you derive from CObject has a static CRuntimeClass data member, provided that you use the MFC DECLARE_DYNAMIC, DECLARE_DYNCREATE, or DECLARE_SERIAL macro in the declaration and the corresponding IMPLEMENT macro in the implementation file The name of the static data member is, by convention, class If your class were named CMyClass, the CRuntimeClass data member would be named classCMyClass If you want a pointer to a class's static CRuntimeClass object, you use the MFC RUNTIME_CLASS macro, defined as follows: #define RUNTIME_CLASS(class_name) (&class_name::class## Here's how you use the macro to get the name string from a class name: ASSERT(RUNTIME_CLASS(CMyClass)->m_lpszClassName == "CMy If you want the class name string from an object, you call the virtual CObject::GetRuntimeClass function The function simply returns a pointer to the class's static CRuntimeClass object, just as earlier the GetClassName function returned the name string Here's the function you'd write for CMyClass: virtual CRuntimeClass* GetRuntimeClass() const { return &classCMyClass; } And here's how you'd call it: ASSERT(pMyObject->GetRuntimeClass()->m_lpszClassName == Dynamic Creation You've already learned that the DECLARE and IMPLEMENT macros add a static CRuntimeClass object to a class If you use the DECLARE_DYNCREATE or DECLARE_SERIAL macro (and the corresponding IMPLEMENT macro), you get an additional static member function CreateObject (distinct from CRuntimeClass::CreateObject) in your class Here's an example: CObject* CMyClass::CreateObject() { return new CMyClass; } Obviously, CMyClass needs a default constructor This constructor is declared protected in wizard-generated classes that support dynamic creation Now look at the code for the CRuntimeClass::CreateObject function: CObject* CRuntimeClass::CreateObject() { return (*m_pfnCreateObject)(); } This function makes an indirect call to the CreateObject function in the target class Here's how you would dynamically construct an object of class CMyClass: CRuntimeClass* pRTC = RUNTIME_CLASS(CMyObject); CMyClass* pMyObject = (CMyClass*)pRTC->CreateObject(); Now you know how document templates work A document template object has three CRuntimeClass* data members initialized at construction to point to the static CRuntimeClass data members for the document, frame, and view classes When CWinApp::OnFileNew is called, the framework calls the CreateObject functions for the three stored pointers A Sample Program Here is the code for a command-line program that dynamically constructs objects of two classes Note that this isn't real MFC code—the CObject class is a simplified version of the MFC library CObject class You can find this code in the dyncreat.cpp file in the \vcpp32\appendb folder #include #define RUNTIME_CLASS(class_name) (&class_name::class## class CObject; struct CRuntimeClass { char m_lpszClassName[21]; int m_nObjectSize; CObject* (*m_pfnCreateObject)(); CObject* CreateObject(); }; // not a true abstract class because there are no pure // virtual functions, but user can't create CObject ob // because of the protected constructor class CObject { public: // not pure because derived classes don't necessari // implement it virtual CRuntimeClass* GetRuntimeClass() const { re // We never construct objects of class CObject, but // use this to get class hierarchy information static CRuntimeClass classCObject; // DYNAMI virtual ~CObject() {}; // gotta have it protected: CObject() { printf("CObject constructor\n"); } }; CRuntimeClass CObject::classCObject = { "CObject", sizeof(CObject), NULL }; CObject* CRuntimeClass::CreateObject() { return (*m_pfnCreateObject)(); // indirect function } class CAlpha : public CObject { public: virtual CRuntimeClass* GetRuntimeClass() const { return &classCAlpha; } static CRuntimeClass classCAlpha; // DYNAMIC static CObject* CreateObject(); // DYNCREA protected: CAlpha() { printf("CAlpha constructor\n"); } }; CRuntimeClass CAlpha::classCAlpha = { "CAlpha", sizeof(CAlpha), CAlpha::CreateObject }; CObject* CAlpha::CreateObject() // static function { return new CAlpha; } class CBeta : public CObject { public: virtual CRuntimeClass* GetRuntimeClass() const { return &classCBeta; } static CRuntimeClass classCBeta; // DYNAMIC static CObject* CreateObject(); // DYNCREA protected: CBeta() { printf("CBeta constructor\n"); } }; CRuntimeClass CBeta::classCBeta = { "CBeta", sizeof(CBeta), CBeta::CreateObject }; CObject* CBeta::CreateObject() // static function { return new CBeta; } int main() { printf("Entering dyncreate main\n"); CRuntimeClass* pRTCAlpha = RUNTIME_CLASS(CAlpha); CObject* pObj1 = pRTCAlpha->CreateObject(); printf("class of pObj1 = %s\n", pObj1->GetRuntimeClass()->m_lpszClassName); CRuntimeClass* pRTCBeta = RUNTIME_CLASS(CBeta); CObject* pObj2 = pRTCBeta->CreateObject(); printf("class of pObj2 = %s\n", pObj2->GetRuntimeClass()->m_lpszClassName); delete pObj1; delete pObj2; return 0; } ... This edition of Programming Microsoft Visual C++ is about 32-bit programming for Microsoft Windows 95, Microsoft Windows 98, and Microsoft Windows NT using the Win32 API If you really need to do 16-bit programming, find an old copy of the second edition of this... While ATL does not replace MFC, we do think it is an important part of Visual C++, so in this edition of Programming Microsoft Visual C++ we have added two chapters that cover the ATL class libraries C++ vs Java In the last couple of years, there has been a great deal of... If you need efficiency and flexibility, choose C++ Who Can Use This Book The product name "Visual C++" misleads some people They think they've bought a pure visual programming system similar to Microsoft Visual Basic, and for the first few days the illusion