Windows & C++: Extern & _Declspec(Dllimport)

Windows & C++: extern & __declspec(dllimport)

extern means that the entity has external linkage, i.e. is visible outside its translation unit (C or CPP file). The implication of this is that a corresponding symbol will be placed in the object file, and it will hence also be visible if this object file is made part of a static library. However, extern does not by itself imply that the symbol will also be visible once the object file is made part of a DLL.

__declspec(dllexport) means that the symbol should be exported from a DLL (if it is indeed made part of a DLL). It is used when compiling the code that goes into the DLL.

__declspec(dllimport) means that the symbol will be imported from a DLL. It is used when compiling the code that uses the DLL.

Because the same header file is usually used both when compiling the DLL itself as well as the client code that will use the DLL, it is customary to define a macro that resolves to __declspec(dllexport) when compiling the DLL and __declspec(dllimport) when compiling its client, like so:

#if COMPILING_THE_DLL
#define DLLEXTERN __declspec(dllexport)
#else
#define DLLEXTERN __declspec(dllimport)
#endif

To answer your specific questions:

  1. Yes, extern alone is sufficient for static libraries.
  2. Yes -- and the declaration also needs an extern (see explanation here).
  3. Not quite -- see above.
  4. You don't strictly need the extern with a __declspec(dllimport) (see explanation linked to above), but since you'll usually be using the same header file, you'll already have the extern in there because it's needed when compiling the DLL.

What is the difference between __declspec( dllimport ) extern C AND extern C __declspec( dllimport )

extern "C" and __declspec(dllimport) are totally orthogonal.

  • extern "C" means that C linkage should be used for that symbol. I.e. no C++ name mangling will be applied. It also limits functions' interfaces to C-compatible things: built-in types, trivial structs, and pointers. Essentially, it tells the compiler and linker that a symbol should be found in the "C way" rather than the "C++ way". extern "C" is generally used for either calling C functions from C++ code or creating a C-compatible interface to C++ code.
  • __declspec(dllimport) tells the linker that a symbol will be loaded dynamically at runtime from a DLL.

The two can be combined as well. A function marked extern "C" __declspec(dllimport) will be dynamically linked and use C-style linkage.

Using dllimport with Visual C++

If you want to link DLL at runtime, you don't need to import symbols at all.

Just declare a function pointer type, then LoadLibrary()/LoadLibraryEx(), and finally retrieve function pointer from GetProcAddress().

Example (error handling is ommitted for brevity):

typedef int (*MyFunct_t)();
auto myDLL = LoadLibrary("mydll.dll");
auto MyFunct = (MyFunct_t)GetProcAddress(myDLL, "MyFunct");
MyFunct();

(this code is only to show general procedure, it was never compiled and tested and could contain typos and syntax errors, feel free to edit this post to fix them)

what does __declspec(dllexport) do , when add it before a import function

First, think about what you need without the macro. If a class or
function is defined in module1, you need to declare it
__declspec(dllexport) in module1, and __declspec(dllimport) in all
of the other modules. Including in the header file where it is
declared.

Since you don't want to maintain two different header files, and you
don't what conditional compilation all over the place, the best solution
is use a conditionally defined macro, e.g.:

#ifdef MODULE1
#define MODULE1_DECL __declspec(dllexport)
#else
#define MODULE1_DECL __declspec(dllimport)
#endif

When invoking the compiler, you only define MODULE1 in the project
module1; you don't define it in any other project. So when compiling
module1, MODULE1_DECL expands to __declspec(dllexport), and when
compiling any other module, it expands to __declspec(dllimport).

Why do we use __declspec(dllimport) when it seems so useless?

From the documentation:

Using __declspec(dllimport) is optional on function declarations, but the compiler produces more efficient code if you use this keyword. However, you must use __declspec(dllimport) for the importing executable to access the DLL's public data symbols and objects. Note that the users of your DLL still need to link with an import library.

Question on DLL Exporting/Importing and Extern on Windows

First, you don't need to import or export typedefs. As long as they're in the header files that both sides use, you're good. You do need to import/export functions and class definitions.

Presumably you use the same header files for both the importing and exporting code, so you could do some makefile magic to define a preprocessor macro on each side, then do something like this:

#if defined( LIBRARY_CODE )
#define MYAPI __declspec(dllexport)
#else
#define MYAPI __declspec(dllimport)
#endif

extern MYAPI void func1();
class MYAPI MyClass {
...
};

Regarding C vs. C++ functions, you can do this:

#if defined( __cplusplus__ ) // always defined by C++ compilers, never by C
#define _croutine "C"
#else
#define _croutine
#endif

extern _croutine void function_with_c_linkage();

Make sure you import this header file from your C++ source file (containing the implementation of this function) or the compiler won't know to give it C linkage.

how does __declspec(dllimport) actually work?

When you use test() in your code the resulting object file is marked as requiring the symbol for test.

You then supply the .lib file for your dll to the linker, the lib contains the symbol for test and contains the required code for loading your dll which includes the name of the dll.

You could supply a different .lib file which also contains the test symbol which would load a different dll.

Detecting missing __declspec(dllimport) in access to exported globals across dll boundaries

The best way is to make it clear in .def file to linker that you export data, not code:

EXPORTS
i1 DATA
i2 DATA

or not use .def at all. It takes precedence and defaults exported symbol to code.

Let's look what happens when you link your plugin to such malformed .lib file. And assume you declare:

   int __declspec(dllimport) i1;
extern int i2;

This means your .obj file will have external dependencies __imp__i1 and _i2. And while the first one will point to the real imported symbol, jmp-stub

   jmp [addr]  ; FF 25 xx xx xx xx

will be generated for the second one ('cause it considered to be the code symbol) which is meant to fix difference between two kinds of call:

   call [addr] ; FF 15 xx xx xx xx
call addr ; E8 xx xx xx xx

Thus, your i2 will actually point to code section address of jmp-stub, so its value gonna be0x????25ff.



Related Topics



Leave a reply



Submit