tags:

views:

1911

answers:

7

I'm stuck on a fix to a legacy Visual C++ 6 app. In the C++ DLL source I have put

extern "C" _declspec(dllexport) char* MyNewVariable = 0;

which results in MyNewVariable showing up (nicely undecorated) in the export table (as shown by dumpbin /exports blah.dll). However, I can't figure out how to declare the variable so that I can access it in a C source file. I have tried various things, including

_declspec(dllimport) char* MyNewVariable;

but that just gives me a linker error:

unresolved external symbol "__declspec(dllimport) char * MyNewVariable" (__imp_?MyNewVariable@@3PADA)

extern "C" _declspec(dllimport) char* MyNewVariable;

as suggested by Tony (and as I tried before) results in a different expected decoration, but still hasn't removed it:

unresolved external symbol __imp__MyNewVariable

How do I write the declaration so that the C++ DLL variable is accessible from the C app?


The Answer

As identified by botismarius and others (many thanks to all), I needed to link with the DLL's .lib. To prevent the name being mangled I needed to declare it (in the C source) with no decorators, which means I needed to use the .lib file.

+4  A: 

extern "C" is how you remove decoration - it should work to use:

extern "C" declspec(dllimport) char MyNewVariable;

or if you want a header that can be used by C++ or C (with /TC switch)

#ifdef __cplusplus
extern "C" {
#endif
declspec(dllimport) char MyNewVariable;
#ifdef __cplusplus
}
#endif

And of course, link with the import library generated by the dll doing the export.

Tony Lee
+4  A: 

Hello,

you must link against the lib generated after compiling the DLL. In the linker options of the project, you must add the .lib file. And yes, you should also declare the variable as: extern "C" { declspec(dllimport) char MyNewVariable; }

botismarius
+2  A: 

I'm not sure who downmodded botismarius, because he's right. The reason is the .lib generated is the import library that makes it easy to simply declare the external variable/function with __declspec(dllimport) and just use it. The import library simply automates the necessary LoadLibrary() and GetProcAddress() calls. Without it, you need to call these manually.

spoulson
+1  A: 

They're both right. The fact that the error message describes __imp_?MyNewVariable@@3PADA means that it's looking for the decorated name, so the extern "C" is necessary. However, linking with the import library is also necessary or you'll just get a different link error.

Graeme Perrow
+1  A: 

@Graeme: You're right on that, too. I think the "C" compiler that the OP is using is not enforcing C99 standard, but compiling as C++, thus mangling the names. A true C compiler wouldn't understand the "C" part of the extern "C" keyword.

spoulson
True. You could put the extern "C" part inside an #ifdef __CPLUSPLUS block to get around that.
Graeme Perrow
A: 

I've never used _declspec(dllimport) when I was programming in Windows. You should be able to simply declare

extern "C" char* MyNewVariable;

and link to the .libb created when DLL was compiled.

Arkadiy
+1  A: 

In the dll source code you should have this implementation so that the .lib file exports the symbol:

extern "C" _declspec(dllexport) char* MyNewVariable = 0;

The c client should use a header with this declaration so that the client code will import the symbol:

extern "C" _declspec(dllimport) char* MyNewVariable;

This header will cause a compile error if #include-ed in the dll source code, so it is usually put in an export header that is used only for exported functions and only by clients.

If you need to, you can also create a "universal" header that can be included anywhere that looks like this:

#ifdef __cplusplus
extern "C" {
#endif
#ifdef dll_source_file
#define EXPORTED declspec(dllexport) 
#else
#define EXPORTED declspec(dllimport) 
#endif dll_source_file
#ifdef __cplusplus
}
#endif

EXPORTED char* MyNewVariable;

Then the dll source code looks like this:

#define dll_source_code 
#include "universal_header.h"

EXPORTED char* MyNewVariable = 0;

And the client looks like this:

#include "universal_header.h"
...
MyNewVariable = "Hello, world";

If you do this a lot, the monster #ifdef at the top can go in export_magic.h and universal_header.h becomes:

#include "export_magic.h"

EXPORTED char *MyNewVariable;
Bart