class _declspec(dllimport) A {
public:
virtual ~A();
};
and instance of class A created via new operator :
A* p = new A() ;
VC 5.0 compiler generates symbol A::`local vftable' and
replaces vftable created by A constructor with this symbol.
A::`local vftable' contains pointers to import table entries which are
resides in module calling new operator.
If calling module is DLL and it dynamicaly unloaded while
instance of A still exists any call through A vftable cause Acces
Violation.
If class A is not dllimport, it's vftable points to it's own code and
everything okay.
Is there anyone who can answer me : Is it a bug or a feature ?
And what it for ?
And is there any switches (or something else) to make compiler not to do it
?
Thanks,
Vadim Egorov.
You export a class with a virtual DTOR. If you dynamicly unload the DLL
(FreeLibrary?) but still have an instance of the class in the EXE, the
EXE crashes?
If that is your question, then the behavior sounds correct. You have
unloaded the DLL so the code is no longer available. Now you try to
call the DTOR, it jumps to the last known address and begins to
execute. The DLL is not there so your now trying to execute memory you
don't own.
--
Why are builders afraid to have a 13th floor
but book publishers aren't afraid to have a Chapter 11?
Just off the top of my head, don't you want
A* p = new A;
???
--
TIME
2470 Island Drive #304
Spring Park, MN 55384
Jim Marshall wrote in article <3377A3...@msn.com.NoSpam>...
Let me see if I can explain. First, the really quick workaround - just
turn off dllimport on the class. It's OK to leave dllexport on the DLL
side.
Consider the following source code:
=============== foo.h ===============
class DLLIMPEXP Foo {
public:
Foo();
virtual ~Foo();
};
============== exe.cpp ==============
#include <stdio.h>
#include <malloc.h>
#define DLLIMPEXP __declspec(dllimport)
#include "foo.h"
void *operator new(size_t sz) {
puts("In EXE's ::operator new");
return malloc(sz);
}
void operator delete(void *p) {
puts("In EXE's ::operator delete");
free(p);
}
void main(void) {
Foo* pFoo = new Foo;
delete pFoo;
}
============== dll.cpp ==============
#include <stdio.h>
#include <malloc.h>
#define DLLIMPEXP __declspec(dllexport)
#include "foo.h"
void *operator new(size_t sz) {
puts("In DLL's ::operator new");
return malloc(sz);
}
void operator delete(void *p) {
puts("In DLL's ::operator delete");
free(p);
}
Foo::Foo() {
puts("in DLL's Foo::Foo()");
}
Foo::~Foo() {
puts("in DLL's Foo::~Foo()");
}
============= repro.bat =============
del dll.dll dll.exp dll.lib exe.exe *.obj 2>nul
cl -LD -MLd dll.cpp
cl -MLd exe.cpp dll.lib
=====================================
In VC++ 4.2, running exe.exe will produce:
In EXE's ::operator new
in DLL's Foo::Foo()
in DLL's Foo::~Foo()
In DLL's ::operator delete
while in VC++ 5.0, it instead outputs
In EXE's ::operator new
in DLL's Foo::Foo()
in DLL's Foo::~Foo()
In EXE's ::operator delete
The difference is in which version of operator delete is called. Further,
the 4.2 version will pop up a CRT assertion when linked with the static
debug CRT lib, as in repro.bat.
In 4.2, "new Foo" in the EXE calls the EXE's version of ::operator new,
then calls Foo's ctor inside the DLL. There, in the DLL, the object's
vftable pointer would be set to a vftable within the DLL. Now, when you've
got a virtual dtor, the dtor slot in the vftable doesn't point to the real
dtor, instead it points to a compiler-generated helper, the deleting dtor,
which first calls the real dtor, then calls operator delete if necessary.
In 4.2, this del-dtor helper would be in the same context as the vftable,
and call operator delete in the same context, so all of these were on the
DLL side of the EXE/DLL boundary. Even though the "delete pFoo;" was in
the EXE, all the actual work was done by calling the del-dtor through the
vftable. If the EXE's operator new was not compatible with the DLL's
operator delete, as in this example, you end up with mismatched heap
allocation/deallocation. If you're lucky, you assert, if not, well, who
knows when the problem shows up.
To fix this bug, in 5.0, we changed things a bit. When you do "new Foo" on
a dllimport class with a virtual dtor, we call ::operator new, then the
ctor, the same as before. But when the ctor returns, we replace the
vftable pointer, pointing instead at a copy of the vftable, the "local
vftable," which has the dtor slot pointing at a local copy of the del-dtor
helper. This helper will call the dtor as normal, but then calls whatever
operator delete is present in the context of the new. That means we get
matching new/delete operators.
The local vftable makes it much safer to do new and delete on opposite
sides of a DLL boundary, which is the bug we were trying to fix. What I
didn't forsee was this problem with the local vftable and associated
executable code being yanked out from under you by a FreeLibrary.
Fortunately, it's pretty easy to avoid the local vftable rewrite. The
rewrite is only done if an entire class is marked __declspec(dllimport),
and the class has a virtual dtor. So you've got three choices:
1) Make the dtor non-virtual. Obviously not recommended, just included for
completeness.
2) Get rid of dllimport. You can still leave dllexport on the DLL side of
things, so you don't have to start using DEF files. All that's important
is that, on the EXE side, the class isn't dllimport. This has a slight
performance hit, in that calls to the non-virtual methods will go through
import thunks instead of being indirect calls through the import table. In
effect, you've got one extra jump. The penalty doesn't affect the virtual
methods, since those will be called through the vtable in the DLL.
3) Get rid of dllimport on the class, and instead mark the individual
methods dllimport. That way, you avoid the import thunk for non-virtual
methods.
...Phil (VC++ compiler developer)
--
The opinions expressed in this message are my own personal views and do not
reflect the official views of Microsoft Corporation. My responses are not
to be considered official technical support or advice. I do this on my on
time. If you need guaranteed response, contact Microsoft's official
technical support channels.
Jim Marshall <Jim_Ma...@msn.com> wrote in article
<3379E9...@msn.com>...
Regards,
Vadim Egorov.
Phil Lucido wrote in article <01bc60c3$4fd946e0$4862389d@philiplu2>...
You can still do this, by marking the individual static item as dllimport,
e.g.
class Foo {
public:
static __declspec(dllimport) int sm_x;
__declspec(dllimport) Foo();
// ...
}
> By the case, is there any way to use imported data without
> declaring them dllimport ang getting linker error : unresolved external ?
Sorry, no. The compiler needs to know data is dllimport, otherwise it
doesn't know to generate code for the extra indirection required. That's
not necessary for functions since the IAT has a code thunk to do the
interaction for you when calling into a DLL. That's not something you can
do with data, though, just calls.
...Phil
> Regards,
> Vadim Egorov.