PRB: CTL3D Cannot Be Used with Shared MFC DLL

ID: Q126719


The information in this article applies to:


SYMPTOMS

The CTL3D documentation "Adding 3-D Effects to Controls" indicates that you should not use CTL3D with the DLL version of the Microsoft Foundation Classes (MFC). If you try to use CTL3D with the MFC DLL, one of the following symptoms may occur:


CAUSE

MFC versions 2.x rely on every control of a certain type having the same window procedure, which is not the case with CTL3D. The window procedure changes depending on whether the control has been subclassed or not.

The problem is that MFC uses a static variable per class to remember the previous window procedure when it subclasses a window that is newly created or one that is being subclassed with CWnd::SubclassWindow. This static variable is returned by the virtual function CWnd::GetSuperWndProcAddr(). If the static is already non-zero and is different from the window procedure that the new window had before subclassing occurred, GetSuperWndProcAddr() will assert.

In release builds, the MFC DLL is shared by multiple applications, so every control of a certain type used by all the MFC applications that use the MFC DLL use the same window procedure. However, if the control is subclassed, the window procedure being used may belong to one of the applications. If this application terminates, controls in other applications will suddenly have an invalid window procedure. Also, if one application uses CTL3D but others do not, the applications that do not use CTL3D may reference the CTL3D window procedure anyway.


RESOLUTION

To work around this behavior, you must override GetSuperWndProcAddr for any windows you intend to subclass that may also be subclassed by CTL3D. In your override, return a pointer to a class member variable that holds the subclassed window procedure for the object's associated window.

For example, if you want to subclass a ComboBox that may also be subclassed by CTL3D, perform these steps:

  1. Derive a class from CComboBox.


  2. Add a member variable, m_lpfnSuper, to the class.


  3. Override GetSuperWndProcAddr for the class and return &m_lpfnSuper.


The class definition will look like this:

   // combo3d.h

   class C3dComboBox : public CComboBox
   {
   public:
      virtual WNDPROC* GetSuperWndProcAddr();
      WNDPROC m_lpfnSuper;
   }; 
The source file for the class will look like this:

   // combo3d.cpp

   #include "combo3d.h"

   WNDPROC* C3dComboBox::GetSuperWndProcAddr()
   {
      return &m_lpfnSuper;
   } 
Now it is safe to subclass and create from this new class, even if CTL3D subclasses the control.

You should do this same procedure for all the control classes you subclass. If you also turn CTL3D effects off for selected dialogs, you should follow this procedure for the CDialog class as well.


STATUS

This behavior is by design.


MORE INFORMATION

MFC 3.0, included with Visual C++ version 2.0, is designed differently. It corrects this problem by adding the member variable m_pfnSuper to the CWnd class. CWnd::GetSuperWndProcAddr() returns the address of this member variable, instead of returning the address of a static member variable. With this change to MFC, it is no longer necessary to override GetSuperWndProcAddr() for every CWnd-derived class.

Additional query words: 1.00 1.50 1.51 2.00 2.50 2.51


Keywords          : kbcode kb16bitonly 
Version           : 1.00 1.50 1.51
Platform          : WINDOWS 
Issue type        : 

Last Reviewed: July 29, 1999