Article ID: 138414 - Last Review: November 21, 2006 - Revision: 3.1

PRB: FromIDispatch Returns NULL for OLE Control

This article was previously published under Q138414
Expand all | Collapse all

SYMPTOMS

Calling CCmdTarget::FromIDispatch() inside a member function of a class derived from COleControl or attempting to call FromIDispatch() passing an IDispatch pointer belonging to an OLE Control, will always return NULL.

CAUSE

CCmdTarget::FromIDispatch() does the following test to determine if the passed IDispatch pointer belongs to a CCmdTarget-derived class or not:
    COleDispatchImpl dispatch;

    ASSERT(*(DWORD*)&dispatch != 0);    // null vtable ptr?
    if (*(DWORD*)lpDispatch != *(DWORD*)&dispatch)
        return NULL;    // not our Idispatch*
				
The problem here is that classes derived from COleControl don't use the COleDispatchImpl implementation of IDispatch directly. They have their own implementation using BEGIN_INTERFACE_PART and END_INTERFACE_PART that delegates to the COleDispatchImpl object in the CCmdTarget base class. Because of this, the v-tables in the previous test won't match and the function will return NULL.

RESOLUTION

It is possible to access the object of the class derived from COleControl given a valid IDispatch pointer. The object can be accessed from another control or outside of a control with the aid of a helper control. A helper control would be necessary because the following formula relies on details that are internal to a COleControl class.

To access the object of the class derived from COleControl successfully, you need to use a formula similar to this one:
   COleControl * pCtrl = (COleControl*)
       ((BYTE*)lpDisp - m_xDispatch.m_nOffset);
					
In this formula, lpDisp is a valid LPDISPATCH, m_xDispatch is the XDispatch object member of COleControl that contains the control's implementation of IDispatch, and m_nOffset is the offset generated by the INIT_INTERFACE_PART macro for IDispatch.

m_xDispatch is generated by the END_INTERFACE_PART macro as follows:
   #define END_INTERFACE_PART(localClass) \ 
      } m_x##localClass; \ 
      friend class X##localClass; \ 
				
In this case, localClass is "Dispatch" so m_x##localClass becomes m_xDispatch.

m_nOffset is generated by the INIT_INTERFACE_PART macro as follows:
    #ifndef _AFX_NO_NESTED_DERIVATION
    #define INIT_INTERFACE_PART(theClass, localClass) \ 
          size_t m_nOffset; \ 
             INIT_INTERFACE_PART_DERIVE(theClass,localClass) \ 

    #define INIT_INTERFACE_PART_DERIVE(theClass, localClass) \ 
          X##localClass() \ 
        { m_nOffset = offsetof(theClass, m_x##localClass); } \ 
m_nOffset becomes offsetof(COleControl, m_xDispatch).
				

STATUS

This behavior is by design.

MORE INFORMATION

This difference between CCmdTarget and COleControl's implementation of IDispatch disappears in Visual C++ version 4.0. COleControl uses COleDispatchImpl in version 4.0 and therefore, FromIDispatch() will work.

APPLIES TO
  • Microsoft Foundation Class Library 4.2, when used with:
    • Microsoft Visual C++ 2.0 Professional Edition
    • Microsoft Visual C++ 4.1 Subscription
    • Microsoft Visual C++ 1.5 Professional Edition
    • Microsoft Visual C++ 1.51
    • Microsoft Visual C++ 1.52 Professional Edition
Keywords: 
kbprb KB138414
Retired KB ArticleRetired KB Content Disclaimer
This article was written about products for which Microsoft no longer offers support. Therefore, this article is offered "as is" and will no longer be updated.
 

Article Translations