How to gain access to member variables of the control derived class from its property page when you use an ActiveX control (143432)



The information in this article applies to:

  • The Microsoft Foundation Classes (MFC), when used with:
    • Microsoft Visual C++ 2005 Express Edition
    • Microsoft Visual C++ .NET (2003)
    • Microsoft Visual C++ .NET (2002)
    • Microsoft Visual C++, 32-bit Professional Edition 6.0
    • Microsoft Visual C++, 32-bit Learning Edition 6.0
    • Microsoft Visual C++, 32-bit Professional Edition 5.0
    • Microsoft Visual C++, 32-bit Enterprise Edition 6.0
    • Microsoft Visual C++, 32-bit Enterprise Edition 5.0
    • Microsoft Visual C++, 32-bit Editions 4.0
    • Microsoft Visual C++, 32-bit Editions 2.2
    • Microsoft Visual C++, 32-bit Editions 2.1
    • Microsoft Visual C++, 32-bit Editions 2.0
    • Microsoft Visual C++ for Windows, 16-bit edition 1.52
    • Microsoft Visual C++ for Windows, 16-bit edition 1.51
    • Microsoft Visual C++ for Windows, 16-bit edition 1.5
    • Microsoft Visual C++ for Windows, 16-bit edition 1.0

This article was previously published under Q143432
Note Microsoft Visual C++ .NET 2002 and Microsoft Visual C++ .NET 2003 support both the managed code model that is provided by the Microsoft .NET Framework and the unmanaged native Microsoft Windows code model. The information in this article applies only to unmanaged Visual C++ code.

SUMMARY

When you are using an ActiveX control, you may experience situations in which you want to call member functions or to gain access to member variables of the control derived class from its associated property page. You can do this by using the array of IDispatch pointers that represent the objects that are manipulated through the property page. (Each property page holds an array of IDispatch pointers.) This article explains in detail how to do this. This article includes a code sample to illustrate this method.

MORE INFORMATION

In an ActiveX control, property sheets let an end user directly manipulate the control's properties. Property sheets display one or more property pages that contain a collection of properties. These properties could belong either to one particular control or to a collection of ActiveX controls.

Each ActiveX control property page is an in-process object. Each property page has its own CLSID that implements the interface IPropertyPage. The IPropertyPage::SetObjects member function is used to provide a property page with pointers to the objects (IUnknowns) that are manipulated by this particular page. (For more information about the SetObjects function, see the OLE Programmer's Reference, Vol. 1.)

The MFC implementation for the IPropertyPage interface stores the object pointers as an array of IDispatch pointers. The pointers represent the controls that are affected by a particular property page. You can access this array by using COlePropertyPage::GetObjectArray. The property pages in MFC use this IDispatch array to apply the changes directly to the controls by creating a COleDispatchDriver-derived class, attaching the IDispatch array to this class, and invoking the SetProperty/GetProperty of COleDispatchDriver to convey the change to the control-derived class.

An ActiveX control that is generated by using the ControlWizard creates a property page that you can use to manipulate the properties of one particular ActiveX control instead of manipulating a collection of controls. Therefore, you can access the control that is associated with a property page by obtaining the previously mentioned IDispatch array in the COlePropertyPage and then calling the static function CCmdTarget::FromIDispatch. The function returns a pointer to the CCmdTarget object that is associated with any one of the IDispatch pointers. The sample code section of this article illustrates this method.

Note Calling CCmdTarget::FromIDispatch for an IDispatch pointer belonging to an ActiveX control will always return NULL in versions before MFC 4.x. For more information about this problem, click the following article number to view the article in the Microsoft Knowledge Base:

138414 FromIDispatch returns NULL for OLE control

This is no longer a problem in versions MFC 4.x.

Sample code

   // The header file of the control-derived class must be included in
   // the same source file.
   #include "myctrl.h"
  // This code is for versions prior to Visual C++ 2005. Comment out this code
  // when you are using the Visual C++ 2005 code.
   CMyCtrl* CMyPropPage::GetControlClass()
   {
     CMyCtrl *pMyCtrl;
     ULONG Ulong;

     // Get the array of IDispatch pointers that is stored in the property page.
     LPDISPATCH FAR *m_lpDispatch = GetObjectArray(&Ulong);

     // Get the CCmdTarget object that is associated with any one of the previous
     // array elements.
     pMyCtrl = (CMyCtrl*) CCmdTarget::FromIDispatch(m_lpDispatch[0]);

     // Cleanup
     return pMyCtrl;
   } 
   // This is the end of the code that is used for versions other than Visual C++ 2005.

   // The following code applies only to Visual C++ 2005. Uncomment this code for Visual C++ 2005. 
   CMyCtrl* CMyPropPage::GetControlClass() 
   {   
      CMyCtrl *pMyCtrl; 
      ULONG Ulong; 
      // Get the array of IDispatch pointers that is stored in the property page.
      LPDISPATCH FAR *m_lpDispatch = GetObjectArray(&Ulong); 
      // Get the CCmdTarget object that is associated with any one of the previous 
      // array elements.
      pMyCtrl = (CMyCtrl*) CCmdTarget::FromIDispatch(m_lpDispatch[0]); 
       if(NULL == pMyCtrl) 
       { 
           LPDISPATCH pDisp; 
           HRESULT hr = lpDispatch[0]->QueryInterface(IID_DTestActix,(void**)&pDisp); 
           ASSERT(SUCCEEDED(hr)); 
           pMyCtrl = (CTestActixCtrl *) CCmdTarget::FromIDispatch(pDisp); 
           pDisp->Release(); 
      } 
      // Clean up.
      return pMyCtrl; 
   } 
   // This is the end of the Visual C++ 2005-specific code.

   // If your control has a public member variable, you can manipulate that variable
   // as follows. (This code uses m_direct_control.)

   void CMyPropPage::OnLButtonDown(UINT nFlags, CPoint point)
      {
        // Directly modify a member variable of the Control class.
        CMyCtrl *pMyCtrl = GetControlClass();
        if (pMyCtrl)
        {
           pMyCtrl->m_direct_control++;
 
           // Display the new value of the variable in a message box.
           char buf[100];
           AfxMessageBox (_itoa (pMyCtrl->m_direct_control, buf, 10));
        }
 
        COlePropertyPage::OnLButtonDown(nFlags, point);
      }
				
In this code, it is assumed that the array of IDispatch pointers that is returned from GetObjectArray holds the same IDispatch pointer because, in a default ControlWizard-generated application, each property page manipulates a particular ActiveX control.

Important Note CCmdTarget::FromIDispatch may return NULL when the control is created with aggregation support. This behavior is noticeable in containers such as Microsoft Visual C++ 6.0 ActiveX Test Container, Microsoft Excel 97, Excel 2000, Microsoft FrontPage 98, and perhaps others. Therefore, the previous method will not work in those containers. A possible workaround is to make the control nonaggregatable by setting the following in the control constructor.
CPropPageAccessCtrl::CPropPageAccessCtrl()
{
    InitializeIIDs(&IID_DPropPageAccess, &IID_DPropPageAccessEvents);

// New code.
// No aggregation, please!  
    m_xInnerUnknown = 0; //Base class COleControl. Set this by using a call to EnableAggregation().
//End of new code.
}
				
Although this workaround will work for Visual C++ 6.0 ActiveX Test Container, this workaround is not an option for containers that only support such aggregatable controls as Excel 97 and Excel 2000. Disabling aggregation would prevent end users from adding the control to an Excel spreadsheet. You could add a long property to the control and then set the long property to the "this" pointer of the control. Then, you could retrieve this property from the page, do a cast on the value to the control's type, and use it. For more information about this method, click the following article number to view the article in the Microsoft Knowledge Base:

205670 How to obtain access to an ActiveX control from its property page


Modification Type:MajorLast Reviewed:5/15/2006
Keywords:kbProperties kbArchitecture kbCtrl kbhowto KB143432 kbAudDeveloper