PRB: FromIDispatch Returns NULL for OLE Control (138414)



The information in this article applies to:

  • The Microsoft Foundation Classes (MFC), when used with:
    • Microsoft Visual C++, 32-bit Editions 2.0
    • Microsoft Visual C++, 32-bit Editions 4.1
    • Microsoft Visual C++ for Windows, 16-bit edition 1.5
    • Microsoft Visual C++ for Windows, 16-bit edition 1.51
    • Microsoft Visual C++ for Windows, 16-bit edition 1.52

This article was previously published under Q138414

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.

Modification Type:MajorLast Reviewed:12/8/2003
Keywords:kbprb KB138414