views:

39

answers:

2

dll export header

extern "C"
void _declspec(dllexport) __stdcall foo();

.def file

EXPORTS
foo         @1

When I build the dll by 64bit build config, I meet this warning.

warning LNK4197: export 'foo' specified multiple times; using first specification

But If I build the dll by 32bit build config, the warning never occurs.
What is the problem? What is the difference.

In dll header for interface, we usually use this technic,

#ifdef EXPORT_DLL
#define BASICAPI _declspec(dllexport)
#else
#define BASICAPI _declspec(dllimport)
#endif //_EXPORT_DLL

But if def file also exists, we always will be meet the warning when we are building 64bit dll.
So, should we write the codes like this?

#ifdef EXPORT_DLL
#define BASICAPI
#else
#define BASICAPI _declspec(dllimport)
#endif //_EXPORT_DLL

It works well. But it's not familiar to me.
Give me any your opinions.

+3  A: 

It's generally not good practise to specify exports twice for the same function. If you already have __declspec(dllexport) then you do not need to specify the export in a .def file as well. Conversely, if you have the export listed in a .def file, then there's no need for a __declspec(dllexport).

I believe the reason for the warning is that in x86 builds, the __declspec(dllexport) is exporting the decorated name with a leading underscore, but the 64-bit compiler does not decorate names with a leading underscore, leading to the duplicate. To verify this, you could look at the 32-bit DLL in Dependency Walker and you should see two exported functions, "foo" and "_foo".

Dean Harding
'64-bit compiler does not decorate names' I didn't know that. That was the reason! Thanks.
Benjamin
By the way, how can I use Italic font in comment box.
Benjamin
It *does* decorate names, it just decorates them differently to the 32-bit compiler (i.e. without the leading underscore). You can export a function twice with different names (that's what I expect is happening in your 32-bit builds) the warning is because in 64-bit builds, both the `__declspec` and the .def file would result in the *same* exported name - so one of them is considered redundant.
Dean Harding
You can do italics the same as the question/answer block, by putting \* around the words.
Dean Harding
@Dean I've understood what you said now. I agree. I've just tried that. In 32bit dll, however, the warning never occurs. And only one function has been exported(foo)
Benjamin
Where is the _foo! The 32 compiler eliminates the spec silently? It never warned to me.
Benjamin
@Benjamin: Hmm, I won't pretend to know all of the intricacies of the C++ linker, so I'm not really sure what would've happened there... that's what I would have expected to see, but I'm not an expert :)
Dean Harding
@Dean Yes, you are. You helped me enough. Thank you very much.
Benjamin
And one more thing, If I add *extern "C"*, amd64 builds doesn't decorate a name, but x86 does(_foo@n) about __stdcall
Benjamin
I guess that's why the amd64 linker shows me the warning.
Benjamin
Iirc the default calling convention for x64 is fastcall, which has different name decoration (none?) than sdcall.
snemarch
+2  A: 

__declspec(dllexport) and .def files are two different ways to export symbols from a dll. You don't need both and should omit ono of them. The __declspec method is far more versatile for c++ programs as it exports names with c++ mangling, allowing overloaded functions to be exported, but conversely that does make the names harder to import via GetProcAddress.

Also, using a generic macro like EXPORT_DLL is dangerous as it means that you can't build a dll, that uses another dll, without the one one dll trying to export all the symbols of both dlls.

DevStudio automatically creates a symbol on dll projects: <PROJECT>_EXPORTS making it easy and safe to create a EXPORT macro:

#ifdef EXPORT
#undef EXPORT
#endif
#ifdef PROJECTNAMEHERE_EXPORTS
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

EXTERN_C EXPORT void __stdcall Function1(void);
EXTERN_C EXPORT void __cdecl Function2(...);
         EXPORT void Function3(void);

Functions 1 & 2 can be gotten with GetProcAddress as _Function1@0 and Function2 respectively. Function3 is going to be exported via a compiler specific mangled name that will look something like: @Function3@@UAG_DB@Z. This name is different for each overload of the function, which is how it allows overloading to work.

Its important to know the name mangling of __declspec as .def files don't care and would just export Function1, Function2 and Function3.

Chris Becke
Your first paragraph is pretty much what I was trying to say with *my* first paragraph, but you said it much better than me, +1 :)
Dean Harding
Nice answer and comment. +1 :)
Benjamin
I conversly didn't know about the 64bit thing.
Chris Becke