Article ID: 131946
Article Last Modified on 11/21/2006
APPLIES TO
- Microsoft Foundation Class Library 4.2, when used with:
- Microsoft Visual C++ 2.0 Professional Edition
- Microsoft Visual C++ 4.0 Standard Edition
This article was previously published under Q131946
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 file 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 file 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:
128199 How to Use _declspec(dllexport) in an MFC Extension DLL
Additional query words: 2.00 2.10 3.00 3.10 4.00
Keywords: kbarchitecture kbdll kbprb KB131946