Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

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).

28fig10.gif

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:

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).

28fig11.jpg

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

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.

Share ThisShare This

Informit Network