PRB: Bad Pointer from RUNTIME_CLASS with Class from _AFXDLL

Last reviewed: July 31, 1997
Article ID: Q131946
2.00 2.10 4.00 WINDOWS NT kbprg kbtshoot kbprb

The information in this article applies to:

  • The Microsoft Foundation Classes (MFC) included with:

        - Microsoft Visual C++ 32-bit Edition, versions 2.0, 2.1, and 4.0
    

SYMPTOMS

An application uses the RUNTIME_CLASS macro to obtain a pointer to the CRuntimeClass associated with a class implemented in an extension DLL. When that pointer is used, an access violation or other unexpected behavior occurs.

CAUSE

The CRuntimeClass data member of the class is not properly declared as imported data.

For example, this error occurs if the CRuntimeClass object is exported via the DLL's module definition (.DEF) file, but AFX_DATA is not defined as __declspec(dllimport). This must be done in the header file containing the class declaration when the .EXE that imports the class is built. This enables the linker to resolve the reference to the CRuntimeClass using the import library for the DLL, so no build-time errors occur. However, the code generated by the compiler doesn't use the import table to determine the address of the item.

RESOLUTION

Ensure that AFX_DATA is defined as __declspec(dllimport) when including the class header file in code for the module that imports the class from the DLL. See the "Sample Code" section of this article for a detailed example.

STATUS

This behavior is by design.

MORE INFORMATION

The DECLARE_DYNAMIC macro declares a CRuntimeClass object as follows:

   static AFX_DATA CRuntimeClass class##class_name;

Therefore, defining AFX_DATA as __declspec(dllimport) affects the declaration of that object. The rest of this section describes why it is important to do so.

The RUNTIME_CLASS macro is defined in afx.h as follows:

   #define RUNTIME_CLASS(class_name) (&class_name::class##class_name)

In other words, this macro returns the address of the static CRuntimeClass member added to the class by the DECLARE_DYNAMIC macro or one of its cousins (DECLARE_SERIAL or DECLARE_DYNCREATE).

Here is some disassembled (X86) code from an EXE that uses a class named CMyClass, which it imports from an extension DLL. The extension DLL uses its module definition file to export the members of the class, and the header file does not declare the class or any of its members as imported.

m_pMyClass has just been initialized as follows:

   m_pMyClass = new CMyClass;

138:      ASSERT( m_pMyClass->IsKindOf(RUNTIME_CLASS(CMyClass)));
00401a9e   push      00402648
00401aa3   mov       eax,dword ptr [this]
00401aa6   mov       ecx,dword ptr [eax+000000d8]
00401aac   call      ?IsKindOf@CObject@@QBEHPBUCRuntimeClass@@@Z ...

The code fails the assertion.

Here is the disassembly of the same code when the header file defines AFX_DATA as __declspec(dllimport):

138:      ASSERT( m_pMyClass->IsKindOf(RUNTIME_CLASS(CMyClass)) );
00401a95   mov       eax,dword ptr
 [_imp_?classCMyClass@CMyClass@@2UCRuntimeClass@@A (00406598)]
00401a9a   push      eax
00401a9b   mov       eax,dword ptr [this]
00401a9e   mov       ecx,dword ptr [eax+000000d8]
00401aa4   call      ?IsKindOf@CObject@@QBEHPBUCRuntimeClass@@@Z ...

This code sequence passes the assertion, as expected.

Notice the difference in the way the stack is set up prior to the call to IsKindOf, especially the first item pushed. In the second case, eax is loaded with the address of an item located through the import table, while in the first case, a direct value is used.

Sample Code

/* Compile options needed: None
*/

// MYCLASS.H
//   The following class declaration illustrates the CORRECT
//   way to declare the AFX_DATA symbol when exporting a class
//   from an extension DLL

#undef AFX_DATA
#define AFX_DATA AFX_EXT_CLASS

class CMyClass : public CObject {
  DECLARE_DYNAMIC()

  // ... other class members omitted for clarity
};

#undef AFX_DATA

#define AFX_DATA

// end of header file

REFERENCES

For additional information on how to export classes from extension DLLs, please see:

  • MFC TechNote #33 in the Books OnLine.
  • The following article in the Microsoft Knowledge Base:

    ARTICLE-ID: Q128199

       TITLE     : How to Use _declspec(dllexport) in an MFC Extension DLL
    


Additional reference words: 2.00 2.10 3.00 3.10 4.00
KBCategory: kbprg kbtshoot kbprb
KBSubcategory: MfcDLL
Keywords : MfcDLL kbprb kbprg kbtshoot
Technology : kbMfc
Version : 2.00 2.10 4.00
Platform : NT WINDOWS


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: July 31, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.