Q138414: PRB: FromIDispatch Returns NULL for OLE Control

Article: Q138414
Product(s): Microsoft C Compiler
Version(s): 2.00 2.10 | 1.50 1.51 1.52 1.52
Operating System(s): 
Keyword(s): kbprb
Last Modified: 30-JUL-2001

-------------------------------------------------------------------------------
The information in this article applies to:

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

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

" (without the quotation marks) 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.

Additional query words: 2.00 2.10 1.50 1.51 1.52 1.52a

======================================================================
Keywords          : kbprb 
Technology        : kbAudDeveloper kbMFC
Version           : 2.00 2.10 | 1.50 1.51 1.52 1.52
Issue type        : kbprb

=============================================================================