- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
ActiveX Controls
As discussed earlier, ActiveX controls originally were conceived as OLE controls that were optimized for use with the Internet and corporate intranets. This does not mean that ActiveX controls can be used only in the Internet environment; quite the contrary—they can be used in any container that can support their interfaces. ActiveX controls still must be embedded in a container application. When you are using a Web browser that supports downloading ActiveX controls, for example, and you encounter a page with an ActiveX control, the control is downloaded to the client if it is not loaded on the local machine. The two most prevalent browsers that support ActiveX controls are Microsoft Internet Explorer and Netscape, with the help of the NCompass plug-in.
The major difference between the older OLE control specification and the newer ActiveX control specification is that many interfaces now are optional. An ActiveX control is expected to support the interfaces required for hosting inside "visual" development tools and must be self registering. Other than these requirements, the control is free to implement interfaces as it sees fit. In contrast, in the previous standard, an OLE control was required to support an armada of COM interfaces, whether or not the control needed them. This made some controls bloated with code that was not used or needed. In the world of Internet development, code bloat is unacceptable.
Supporting the IUnknown Interface
As discussed earlier, all COM objects support the IUnknown interface. As discussed in Chapter 24, "Overview of COM and Active Technologies," IUnknown is an interface that supports three methods: QueryInterface, AddRef, and Release.
All COM interfaces are inherited either directly or indirectly from IUnknown; hence, all other interfaces have these three functions also. With a pointer to IUnknown, a client can get a pointer to other interfaces the object supports through QueryInterface. In short, an object can use QueryInterface to find out the capabilities of another object. If the object supports the interface, it returns a pointer to the interface. If the object does not support the interface, E_NOINTERFACE is returned. Listing 28.5 demonstrates the use of the pointer to a control's IUnknown interface and QueryInterface to find out the class information using MFC.
Example 28.5. Using a Pointer to IUnknown and QueryInterface to Retrieve Class Information
1: // Function to get a pointer to a control's IUnknown and use
2: // QueryInterface to see if it supports the interface.
3: int MyClass::DoControlWork()
4: {
5: LPUNKNOWN lpUnknown;
6: LPPPROVIDECLASSINFO lpClassInfo;
7:
8: lpUnknown = GetControlUnknown();
9:
10: if(lpUnknown == NULL)
11: {
12: // return the common error code for bad pointers
13: return E_POINTER;
14: }
15: else
16: {
17: if(SUCCEEDED(lpUnknown->QueryInterface(IID_IProvideClassInfo,
18: (void**) &lpClassInfo)))
19: {
20: // QueryInterface Returned a Succeeded so this
21: // Interface is Supported
22: // {
23: //Perform some function with lpClassInfo such
24: // as getting the class info and examining the class attributes
25: // {
26:
27: lpClassInfo->Release();
28: }
29: else
30: {
31: // Control Does Not Support Interface
32: return E_NOINTERFACE;
33: }
34: }
35: return NO_ERROR;
36: }
In addition, the object can manage its own lifetime through the AddRef and Release functions. If an object obtains a pointer to an object, AddRef is called, incrementing the object's reference count. After an object no longer needs the pointer to the interface, Release is called, decrementing the object's reference count. When the reference count reaches zero, an object created on the heap can safely destroy itself.
Although the IUnknown interface is required for all COM objects, you also should take a look at the other interfaces an ActiveX control may choose to implement, as shown in Table 28.4.
In addition, the control may implement its own custom interfaces. By implementing only the interfaces it needs, the ActiveX control can be as lean as possible.
Table 28.4. Potential COM Interfaces for an ActiveX Control
| Interface | Purpose |
| IOleObject | Principal mechanism by which a control communicates with its container. |
| IOleInPlaceObject | Manages activation and deactivation of an object. |
| IOleInPlaceActiveObject | Provides communications between an in-place active object and the outermost windows of the container. |
| IOleControl | Allows support for keyboard mnemonics, properties, and events. |
| IDataObject | Allows for the transfer of data and the communication of changes in the data. |
| IViewObject | Allows the object to display itself. |
| IViewObject2 | Allows you to find the size of the object in a given view. An extension of the IViewObject interface. |
| IDispatch | Enables you to call virtually any other COM interface. Used in OLE automation to evoke late binding to properties and methods of COM objects. |
| IConnectionPoint Container | Supports connection points for connectable objects. |
| IProvideClassInfo | Encapsulates a single method by which to get all the information about an object's co-class entry in its type library. |
| IProvideClassInfo2 | Provides quick access to an object's Interface ID (IID) for its event set. An extension to IProvideClassInfo. |
| ISpecifyPropertyPages | Denotes an object as supporting property pages. |
| IPerPropertyBrowsing | Supports methods to get access to the information in the Properties pages supported by an object. |
| IPersistStream | Provides methods for loading and storing simple streams. |
| IPersistStreamInit | Adds an initialization method, InitNew. Designed as a replacement for IPersistStream. |
| IPersistMemory | Allows the method to access a fixed-sized memory block for an IPersistStream object. |
| IPersistStorage | Supports the manipulation of storage objects to include loading, saving, and exchanging. |
| IPersistMoniker | Exposes to asynchronous objects the capability to manipulate the way they bind data to the object. |
| IPersistPropertyBag | Allows the storage of persistent properties. |
| IOleCache | Controls access to the cache inside an object. |
| IOleCache2 | Allows the selective update of an object's cache. |
| IExternalConnection | Allows the tracking of external locking on an embedded object. |
| IRunnableObject | Enables a container to control its executable objects. |
A Control Must Be Self-Registering
Before you can use an ActiveX control or any other COM object, it must be registered in the System Registry. The System Registry is a database of configuration information divided into a hierarchical tree. This tree consists of three levels of information: hives, keys, and values. The System Registry is a centralized place where you can go to get information about an object (see Figure 28.10).
Figure 28.10 The Windows 98 System Registry as seen through the Regedit program.
If the control is not registered in the Registry, it is unknown and therefore unusable by the system. If it's not in the Registry, the rest of the system doesn't know it's there.
Thus, it is a requirement for ActiveX controls to be self-registering. This means an ActiveX control must implement and export the functions DllRegisterServer and DllUnregisterServer. In addition, it is a requirement for ActiveX controls to register all the standard Registry entries for automation servers and embeddable objects. Listing 28.6 demonstrates the use of DllRegisterServer to support self-registration of the control using MFC. Visual C++'s AppWizard generates this code for you.
Example 28.6. Using the DllRegisterServer to Support Self-Registration of the Control
1: ////////////////////////////////////////////////////////////
2: // DllRegisterServer - Adds entries to the system registry
3:
4: STDAPI DllRegisterServer(void)
5: {
6: AFX_MANAGE_STATE(_afxModuleAddrThis);
7:
8: if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
9:return ResultFromScode(SELFREG_E_TYPELIB);
10:
11: if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
12:return ResultFromScode(SELFREG_E_CLASS);
13:
14: return NOERROR;
15: }
Listing 28.7 demonstrates the use of DllUnregisterServer to support self-unregistration of a control using MFC. Visual C++'s ControlWizard generates this code for you.
Example 28.7. Using DllUnregisterServer to Support Self-Unregistration of a Control
1: ///////////////////////////////////////////////////////
2: // DllUnregisterServer - Removes entries from the
system registry
3:
4: STDAPI DllUnregisterServer(void)
5: {
6: AFX_MANAGE_STATE(_afxModuleAddrThis);
7:
8: if (!AfxOleUnregisterTypeLib(_tlid))
9: return ResultFromScode(SELFREG_E_TYPELIB);
10:
11: if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
12: return ResultFromScode(SELFREG_E_CLASS);
13:
14: return NOERROR;
15: }
Listings 28.6 and 28.7 show how you support registration and unregistration, and Listing 28.8 shows how you register your control and its capabilities. Notice in line 15 of Listing 28.8 the variable dwMyControlOleMisc. It contains the status bits of your control. This is very important because it contains the capabilities of your control. These capabilities can be looked up in the System Registry to find out what capabilities your control contains without instantiating the object.
Example 28.8. Registering Your Control and Your Control's Capabilities in MFC
1: ////////////////////////////////////////////////////////////////////
2: // CMyCtrl::CMyCtrlFactory::UpdateRegistry -
3: // Adds or removes system registry entries for CMyCtrl
4: BOOL CMyCtrl::CMyCtrlFactory::UpdateRegistry(BOOL bRegister)
5: {
6: if (bRegister)
7: return AfxOleRegisterControlClass(
8: AfxGetInstanceHandle(),
9: m_clsid, // Records the Object's CLSID
10: m_lpszProgID, // Records a Unique Program ID for MyControl
11: IDS_MYCONTROL, // Records a Human Readable Name of MyControl
12: IDB_MYCONTROL, // Records the Bitmap to Represent MyControl
13: TRUE,// Records that MyControl can be insertable
14:// in a Container's Insert Object Dialog
15: dwMyControlOleMisc, // Records the Status bits of MyControl
16: tlid,// Records the Unique ID of the MyControls
17:// Control Class
18: wVerMajor, // Records the Major Version of MyControl
19: wVerMinor); // Records the Minor Version of MyControl
20: else
21:return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
22: }
Table 28.5 lists the possible status bits that can be set for a control. These bits identify the capabilities of the control.
Table 28.5. The OLE Miscellaneous Status Bits Symbolic Constants and What They Mean to Controls and Objects
| Symbolic Constant | Meaning |
| OLEMISC_RECOMPOSEONRESIZE | Identifies an object that, upon resizing by the container, will rescale its presentation data. |
| OLEMISC_ONLYICONIC | Identifies an object that only exists in the iconic state. |
| OLEMISC_INSERTNOTREPLACE | Identifies an object that initializes itself from the currently selected container data. |
| OLEMISC_STATIC | Identifies that an object is static and contains no native data—only presentation data. |
| OLEMISC_CANTLINKINSIDE | Identifies items such as OLE 1.0 objects, static objects, and links. These are objects that cannot be a linked source object. In addition, when the object is bound, it cannot run another object. |
| OLEMISC_CANLINKBYOLE1 | Identifies that an object can be linked by the containers that conform to the OLE 1.0 specification. |
| OLEMISC_ISLINKOBJECT | Identifies that an object is a linked object. This is only important for OLE 1.0 objects. |
| OLEMISC_INSIDEOUT | Identifies that an object can be in-place activated without the need for toolbars or menus. |
| OLEMISC_ACTIVATEWHENVISIBLE | Identifies that an object can be activated only in the visible state. The OLEMISC_INSIDEOUT flag also must be set. |
| OLEMISC_RENDERINGISDEVICEINDEPENDENT | Identifies that the object's presentation data will remain the same, regardless of the target container. |
| OLEMISC_INVISIBLEATRUNTIME | Identifies controls that are invisible at runtime, such as Internet Explorer's Timer control or Internet Explorer's PreLoader control. |
| OLEMISC_ALWAYSRUN | Tells a control that a control should be set in the running state even when not visible. |
| OLEMISC_ACTSLIKEBUTTON | Identifies controls that can act like buttons. |
| OLEMISC_ACTSLIKELABEL | Identifies controls that can change the label provided by the container. |
| OLEMISC_NOUIACTIVATE | Identifies whether a control supports user- interface activation. |
| OLEMISC_ALIGNABLE | Identifies that a control can be aligned with other controls for containers that support control alignment. |
| OLEMISC_SIMPLEFRAME | Identifies that the control supports the ISimpleFrameSite interface. |
| OLEMISC_SETCLIENTSITEFIRST | In the new OLE container specification, identifies controls that support the SetClientSide function being called after the control is created but before it is displayed. |
| OLEMISC_IMEMODE | In the Double Byte Character Set (DBCS) versions of Windows, identifies that the control supports the Input Method Editor mode for internationalized controls. |
These miscellaneous status bits are especially important when used with component categories as an accurate picture of what your control can or cannot do. You can obtain this picture of what the control can do from the System Registry.
Component Categories
Previously, in order to be registered on the system, an ActiveX control was registered through entries in the Registry with the Control keyword. To your benefit, controls can be used for multiple purposes. Therefore, a way was needed to identify a control's functionality as opposed to just listing the interfaces it supports. This is where component categories come in.
Component categories are a way of describing what a control does. They provide a better method for containers to find out what a control does without creating it and having to query for its methods using an IUnknown pointer and QueryInterface. Creating a control object involves a great deal of overhead. A container would not want to create a control if the container itself does not support the functionality the control requires.
Component categories are not specific to ActiveX but are an extension of the COM architecture. Each component category has its own GUID and a human-readable name stored in a well-known place in the System Registry. When a control registers itself, it does so using its component category ID. In addition, it registers the component categories it supports and the component categories it requires its container to support.
For backward compatibility, the control also should register itself with the Control keyword for containers that do not support the new component categories. The control also should register the key ToolBoxBitmap32. This key identifies the module name and resource ID for a 16x15 bitmap. ToolBoxBitmap32 provides a bitmap to use for the face of a toolbar or toolbox button in the container application. If a control can be inserted in a compound document, it also should register the Insertable key.
Component categories can be mixed and matched depending on their type. Microsoft maintains a list of component categories. Any categories that are new should be submitted to Microsoft for inclusion in the list. This promotes interoperability. The following component categories have been identified:
- Simple Frame Site Containment
- Simple Data Binding
- Advanced Data Binding
- Visual Basic Private Interfaces
- Internet-Aware Controls
- Windowless Controls
This list is not all-inclusive. The following sections give you more information on these component categories.
Simple Frame Site Containment
A simple frame site container control contains other controls—for example, a 3D group box that contains a group of check boxes. The GUID for this component category is
CATID - {157083E0-2368-11cf-87B9-00AA006C8166} CATID_SimpleFrameControl
To support a simple frame site container, the container application must implement the ISimpleFrameSite interface, and the control must have its status bit set to OLEMISC_SIMPLEFRAME.
Simple Data Binding
A control or container that supports simple data binding supports the IPropertyNotifySink interface. Data binding is how controls affiliate their persistent properties and how containers exchange property changes from their user interface to the control's persistent properties. This capability allows the persistent storage of their properties and at runtime binds the data to the control synchronizing property changes between the control and the container. The GUID for this component category is
CATID - {157083E1-2368-11cf-87B9-00AA006C8166} CATID_PropertyNotifyControl
Advanced Data Binding
Advanced data binding is similar to simple data binding except that it supports more advanced binding techniques, such as asynchronous binding and Visual Basic data binding. The GUID for this component category is
CATID - {157083E2-2368-11cf-87B9-00AA006C8166} CATID_VBDataBound
Visual Basic Private Interfaces
These component categories are for components that specifically support the Visual Basic environment. Controls or containers that use these categories can support alternative methods. This capability is useful if a container encounters a control—or a control encounters a container—that does not support the Visual Basic private interface categories. The GUID for this component category is
CATID - {02496840-3AC4-11cf-87B9-00AA006C8166} CATID_VBFormat
if the container implements the IVBFormat interface for data formatting to specifically integrate with Visual Basic, or
CATID - {02496841-3AC4-11cf-87B9-00AA006C8166} CATID_VBGetControl
if the container implements IVBGetControl so that controls can enumerate other controls on a Visual Basic form.
Internet-Aware Controls
Internet-aware controls implement one or more persistent interfaces to support operation across the Internet. All these categories provide persistent storage operations. The following are GUIDs for components that fall into this category:
CATID - {0de86a50-2baa-11cf-a229-00aa003d7352} CATID_RequiresDataPathHost
CATID - {0de86a51-2baa-11cf-a229-00aa003d7352} CATID_PersistsToMoniker
CATID - {0de86a52-2baa-11cf-a229-00aa003d7352} CATID_PersistsToStorage
CATID - {0de86a53-2baa-11cf-a229-00aa003d7352} CATID_PersistsToStreamInit
CATID - {0de86a54-2baa-11cf-a229-00aa003d7352} CATID_PersistsToStream
CATID - {0de86a55-2baa-11cf-a229-00aa003d7352} CATID_PersistsToMemory
CATID - {0de86a56-2baa-11cf-a229-00aa003d7352} CATID_PersistsToFile
CATID - {0de86a57-2baa-11cf-a229-00aa003d7352} CATID_PersistsToPropertyBag
The RequiresDataPathHost category means that the object requires the container to support the IBindHost interface, because the object requires the capability to save data to one or more paths.
All the rest of the categories listed are mutually exclusive. They are used when an object supports only a single persistence method. If a container does not support a persistence method that a control supports, the container should not allow itself to create controls of that type.
Windowless Controls
Windowless controls are controls that do not implement their own window and rely on the use of their container's window when drawing themselves. These types of controls include nonrectangular controls, such as arrow buttons, gauges, and other items modeled after real-world objects. In addition, this category includes transparent controls. The GUID for this component category is
CATID - {1D06B600-3AE3-11cf-87B9-00AA006C8166} CATID_WindowlessObject
Component Categories and Interoperability
Components that do not support a category should degrade gracefully. In the case where a control or container is unable to support an interface, the control should either clearly document that a particular interface is required for the proper operation of the component or at runtime notify the user of the component's degraded capability.
By using self-registration, components can be self-contained. By using DllRegisterServer and DllUnregisterServer and the component category's API functions to register itself and the component categories it supports, a control can further its interoperability in a variety of environments.
Code Signing
In the Internet environment, users must download the components to their local machine and use them. Allowing the implementation of this foreign code poses an extreme hazard to the local machine.
This is where a security measure called code signing comes in. Browsers typically warn users that they are downloading a potentially unsafe object; however, the browser does not physically check the code for authenticity to ensure that it has not been tampered with, nor does it verify its source.
Microsoft has implemented Authenticode, which embodies the Crypto API. This feature enables developers to digitally sign their code so that it can be checked and verified at runtime. This function is built into the browser and displays a certificate of authenticity if the control is verified (see Figure 28.11).
Figure 28.11 The certificate the user sees at runtime after the code is authenticated.
Currently, the code-signing specification and the certification process are being reviewed by the World Wide Web Consortium (W3 Consortium), and the current specifications are subject to change. Internet Explorer and all Microsoft controls naturally support code signing and Authenticode, but Netscape supports a different type of code-signing technology, known as object signing.
Code signing works with DLLs, EXEs, Cabinet (CAB) files, and ActiveX controls. A developer who creates these items attains a digital certificate from an independent certification authority. The developer then runs a one-way hash on the code and produces a digest that has a fixed length. Next, the developer encrypts the digest using a private key. This combination of an encrypted digest coupled with the developer's certificate and credentials is a signature block unique for the item and the developer. This signature block is embedded in the executable program.
Here's the way code signing works on the client machine. When a user downloads a control from the Internet, the browser application, such as Internet Explorer or Netscape, calls a Win32 API function called WinVerifyTrust.
WinVerifyTrust then reads the signature block. With the signature block, the WinVerifyTrust can authenticate the certificate and decrypt the digest using the developer's public key. Using the public key, the function then rehashes the code with the hash function stored in the signature block and creates a second digest. This digest then is compared with the original. If the digests do not match, this indicates tampering and the user is warned.
The code-signing mechanism provides some security for end users and developers alike. It is a deterrent to malicious tampering with executable code for the intent of information warfare, such as viruses, and it is also a deterrent for those who can pirate code devel oped by others. Please be aware again that this is a proposed standard and has not yet been accepted officially, although there is nothing I can see at this time that can compete with it. It is safe to say that no matter what, Microsoft will continue to support and refine this standard. The bottom line is that you will need to continue to monitor the standard.
Performance Considerations
ActiveX controls are designed to work across the Internet. As such, they are Internet-aware. Unfortunately, the Internet is low bandwidth and highly subject to server latency. This means that ActiveX controls must be lean and mean, or to put it more plainly, highly optimized. Because ActiveX controls implement only the interfaces they need, they already are partially optimized. ActiveX controls are optimized to perform specific tasks. However, you can do several things to help optimize your controls:
- Optimize control drawing.
- Don't always activate your control when it is visible.
- Provide flicker-free activation.
- Optimize persistence and initialization.
- Use windowless controls.
- Use a device context that is unclipped.
- While a control is inactive, provide mouse interaction.
Optimize Control Drawing
When you draw items, you have to select items such as pens, brushes, and fonts into the device context to render an object onscreen. Selecting these items into the device context requires time and is a waste of resources when the container has multiple controls that are selecting and deselecting the same resources every time they paint. The container can support optimized drawing. This means that the container handles the restoration of the original objects after all the items have been drawn. IViewObject::Draw supports optimized drawing by using the DVASPECTINFOFLAG flags set in the DVASPECTINFO structure. You must use this structure to determine whether your container supports optimized drawing when implementing API functions. MFC encapsulates this check for you in the COleControl::IsOptimizedDraw function. You then can optimize how you draw your code by storing your GDI objects as member variables instead of local variables. This method prevents your objects from being destroyed when the drawing function finishes. Then, if the container supports optimized drawing, you do not need to select the objects back, because the container has taken care of this for you.
Don't Always Activate Your Control When It Is Visible
If your control has a window, it might not need to be activated when visible. Creating a window is a control's single biggest operation and should not be done until it is absolutely necessary. Therefore, if there is no reason for your control to be activated when visible, turn off the OLEMISC_ACTIVATEWHENVISIBLE miscellaneous status bit.
Provide Flicker-Free Activation
When your control has a window, it sometimes must transition from the active to the inactive state. A visual flicker occurs when the control redraws from the active to the inactive state. Two methods—drawing off-screen and then copying to the screen in one big chunk, and drawing front to back—can eliminate flicker. The IViewObjectEx API function provides the necessary functions to use either method or a combination of both. With MFC, the implementation is much simpler—simply do what is shown in Listing 28.9.
Example 28.9. Setting the noFlickerActive Flag in MFC
1: DWORD CMyControl::GetControlFlags()
2: {
3: return COleControl::GetControlFlags() | noFlickerActivate;
4: }
Optimize Persistence and Initialization
Optimizing persistence and initialization means basically one thing: Keep your code as lean as possible. Because of the cheapness of hard drive space and memory, some programmers have gotten lazy when creating this code and have allowed it to become bloated and slow. With Internet applications, this is a death sentence. Most people access the Internet with dial-up modems. One megabyte of data takes almost nine minutes to transfer over a 14.4 Kbps modem. Users become impatient if they have to wait long periods of time. You can do several things to solve this problem.
First of all, make sure you do not leave any unused blocks of code or variables. You also should take any debugging or testing blocks out of your code. Suppose that you have written your code so a message box displays when you reach a certain segment of code. Take it out! It only adds to your code size. However, if you delimit your debugging blocks of code using the preprocessor #ifdef _DEBUG and #endif, you will not have to worry about the code being included in the release builds, because the debugging blocks of code will be left out of the compile.
Second, today's compilers have optimizing options on them. In the past, these optimizing compilers were not very efficient and sometimes introduced bugs into an application that already had been tested. But compilers have gotten much better. Use them! Let the compiler do some of the work for you. You might have to tweak and play with the optimizations to find the best combination of options.
You also should turn off the incremental linking option on your compiler when you do a release build. Incremental linking can add serious bloat to your code.
The final thing you should consider is using asynchronous operations to perform initialization and persistence operations. Asynchronous downloading gives users the illusion that things are occurring faster than they are. In addition, you might want to give users other visual cues that progress is being made, such as a progress indicator or a message box. However, you will have to weigh the performance issues associated with those additions.
Use Windowless Controls
You should consider making your control a windowless control if appropriate. Creating a window is a control's single biggest operation, taking almost two-thirds of its creation time. This is a lot of unnecessary overhead for the control. Most of the time, a control does not need a window and can use its container's window and allow the container to take on the overhead of maintaining that window. Using windowless controls enables you to model your controls on real-world objects, such as gauges, knobs, and other nonrectangular items.
By using the API function IOleInPlaceSiteEx::OnInPlaceActivateEx and setting the ACTIVATE_WINDOWLESS flag, you can have your control be in windowless mode. Listing 28.10 demonstrates how you can do this with MFC.
Example 28.10. Setting the Windowless Flag in MFC
1: DWORD CMyControl::GetControlFlags()
2: {
3: return COleControl::GetControlFlags() | windowlessActivate;
4: }
In addition, a whole series of API functions allows you to manipulate windowless controls. MFC also has encapsulated many of these functions for you. The MSDN Library included with Visual C++ has a complete reference for these functions. In addition, the Win32 API references have the API-level functions. For more information, launch the MSDN Library by selecting Help|Search from the Developer Studio menu, then search for "windowless controls".
Use a Device Context That Is Unclipped
If you have a window and you are sure your control does not draw outside of that window, you can disable the clipping in your drawing of the control. You can yield a small performance gain by not clipping the device context. With MFC, you can do what is shown in Listing 28.11 to remove the clipPaintDC flag.
Example 28.11. Setting the clipPaintDC Flag in MFC
1: DWORD CMyControl::GetControlFlags()
2: {
3: return COleControl::GetControlFlags() & ~clipPaintDC;
4: }
With the API functions in the Platform Software Development Kit, you can implement the IViewObject, IViewObject2, and IViewObjectEx interfaces to optimize your drawing code so that you do not clip the device context.
While a Control Is Inactive, Provide Mouse Interaction
You can set your control to inactive because it does not always need to be activated when visible. You still might want your control to process mouse messages such as WM_MOUSEMOVE and WM_SETCURSOR, though. You will need to implement the IPointerInactive interface to allow you to process the mouse messages. If you are using MFC, you just need to implement the following function, and the framework handles the rest for you. Listing 28.12 shows you how to notify the framework that your control is interested in receiving mouse messages while inactive.
Example 28.12. Setting the Pointer Inactive Flag in MFC
1: DWORD CMyControl::GetControlFlags()
2: {
3: return COleControl::GetControlFlags() | pointerInactive;
4: }
However, you will need to override the OLEMISC_ACTIVATEWHENVISIBLE miscellaneous status bit with OLEMISC_IGNOREACTIVATEWHENVISIBLE. This is because OLEMISC_ACTIVATEWHENVISIBLE forces the control to always be activated when visible. You have to do this to prevent the flag from taking effect for containers that do not support the IPointerInactive interface.
Reinventing the Wheel | Next Section

Account Sign In
View your cart