BUG: Wrong Operator Delete Called for Exported ClassLast reviewed: December 11, 1997Article ID: Q122675 |
The information in this article applies to:
SYMPTOMSWhen allocating and deleting an object of a class that is exported from a DLL, you may find that the new operator in the EXE is called to allocate the memory but the delete operator in the DLL is called to delete the memory. Therefore, there is a new/delete mismatching problem, which may cause run-time errors.
CAUSEThis problem is a result of the way objects are allocated, constructed, destructed and deallocated. The following things occur when allocating and deallocating objects that have constructors and destructors: Allocating:
A1. The memory that the object will reside in is allocated. A2. The appropriate constructor for that object is called.Deallocating:
D1. The destructor for the object is called. D2. If the object is being deallocated via delete, call delete.Step D2 is where the problem arises for objects created from classes exported from a DLL. Steps D1 and D2 are often carried out by a scalar or vector deleting destructor, which are a compiler generated helper functions. When this helper function is created in the DLL, any calls it makes to the delete operator will be in the context of the DLL. Therefore the delete operator overridden in the EXE will not be called as expected. Note that the scalar deleting destructor is called when deallocating single objects, and the vector deleting destructor is called when deallocating arrays of one or more objects.
RESOLUTIONYou can use one of the following workarounds:
STATUSMicrosoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. We are researching this bug and will post new information here in the Microsoft Knowledge Base as it becomes available.
MORE INFORMATIONThe following sample code demonstrates the problem and provides the workarounds mentioned above in the RESOLUTION section.
SAMPLE CODE
Use the following files to build two projects, one for DLL and one for EXE. In the DLL project, include file testdll.cpp and define preprocessor identifier _DLL. In the EXE project, include file testapp.cpp. testdll.h is the shared header file for both DLL and EXE. Make sure to define preprocessor identifier _WORKAROUND in both the EXE and the DLL projects if you want to work around the problem. For example, using the compiler switch /D_WORKAROUND. The problem code produces the following output:
In EXE global new In DLL class constructor In DLL class destructor In DLL global deleteThe workaround code produces the followingoutput:
In DLL class new In DLL class constructor In DLL class destructor In DLL class delete /* Compile options needed: * testapp - /MD and default settings for Console application. * testdll - /MD, /D_DLL, and default setting for DLL project. * Use /D_WORKAROUND in both the EXE and DLL projects to see the * work-around. */ testdll.h
#include <iostream.h> #include <stdlib.h> #ifdef _DLL #define DLLEXP __declspec(dllexport) #else #define DLLEXP __declspec(dllimport) #endif // For use with the 16-bit versions, you need to use the following code // to define DLLEXP. Also start by using a QuickWin application for // testapp and a DLL project for testdll //#ifdef _DLL //#define DLLEXP __export //#else //#define DLLEXP //#endif class DLLEXP testdll { public: testdll(); virtual ~testdll(); #ifdef _WORKAROUND void* operator new( size_t tSize ); void operator delete( void* p ); #endif }; testapp.cpp
#include <iostream.h> #include "testdll.h" void* operator new(size_t nSize) { void* p=malloc(nSize); cout << "In EXE global new\n"; return p; } void operator delete( void *p ) { free(p); cout << "In EXE global delete\n"; } void main() { testdll *p = new testdll; delete p; } testdll.cpp
#include <iostream.h> #include "testdll.h" DLLEXP testdll::testdll() { cout << "In class DLL constructor\n"; } DLLEXP testdll::~testdll() { cout << "In class DLL destructor\n"; } void* operator new( size_t tSize ) { void* p=malloc(tSize); cout << "In DLL global new\n"; return p; } void operator delete( void* p ) { free(p); cout << "In DLL global delete\n"; } #ifdef _WORKAROUND DLLEXP void* testdll :: operator new( size_t tSize ) { void* p=malloc(tSize); cout << "In DLL class new\n"; return p; } DLLEXP void testdll :: operator delete( void* p ) { free(p); cout << "In DLL class delete\n"; } #endif The following code fragment demonstrates how you might implement a template wrapper class in the EXE to work around this problem:
template <class T> class DLLFix: public T { public: DLLFix(); virtual ~DLLFix(); };Assuming that ExportedClass is a class that is exported from a DLL, make the following change:
//Old code ExportedClass * p = new ExportedClass; delete p; // wrong delete may be called here; //new code DLLFix<ExportedClass> * p = new DLLFix<ExportedClass>; delete p; |
Additional query words: _USRDLL _AFXDLL 8.00 9.00 GPF general protection
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |