Version 1 of DLLEXPORT and DLLIMPORT

Updated 2005-12-17 02:38:58

Unlike most Unices, on Windows you need to explicitly specify which functions are publically visible when creating a shared library.

One way to do this is to write a .DEF file listing the symbols to be exported, and pass that to the linker when building the .DLL. (See the linker documentation for details if you're interested; but the next way is easier).

The easier way is to use an MSVC language extension to declare publically visible functions in the source code:

    extern int __declspec(dllexport) Foo_Init(Tcl_Interp *);
    ...
    int __declspec(dllexport) Foo_Init(Tcl_Interp *interp)
    {
        ...
    }

Most other compilers for Windows (including the MinGW and CYGWIN versions of gcc) have adopted this extension.

Naturally, the __declspec(dllexport) bit should be hidden inside a preprocessor macro for cross-platform portability. Tcl.h defines such a macro: the symbol DLLEXPORT expands to the appropriate Windows voodoo if the compilation environment supports it, and to the empty string otherwise.

    int DLLEXPORT Foo_Init(Tcl_Interp *interp) { ... }

That should be sufficient for Tcl extensions' _Init() and _SafeInit() routines. In the general case, though, more preprocessor trickery is needed.

Windows also requires that functions appearing in an external shared library be declared with the "__declspec(dllimport)" storage class. Instead of creating two copies of each header file -- one with __declspec(dllimport) for users of the library and one with __declspec(dllexport) for the library itself -- the usual idiom is to say:

        #ifdef BUILD_FOO
        #    define FOOAPI __declspec(dllexport)
        #else
        #    define FOOAPI __declspec(dllimport)
        #endif
        ...
        extern int FOOAPI foo_fiddle();
        extern int FOOAPI foo_faddle();
        extern int FOOAPI foo_twaddle();

in the relevant header files, then compile the library with -DBUILD_FOO. Tcl does this, too; the macro TCL_STORAGE_CLASS expands to __declspec(dllexport) when building Tcl on Windows, __declspec(dllimport) when linking directly against the Tcl shared library, and to the empty string when linking with the Stubs library and on non-Windows platforms.

Many Tcl extensions use preprocessor contortions like:

  #undef TCL_STORAGE_CLASS
  #ifdef BUILD_sampleextension
  #  define TCL_STORAGE_CLASS DLLEXPORT
  #endif
  ...
  EXTERN int Sample_Init();
  ...

This is overly complex and error-prone; I recommend not doing this. Basically, this is hijacking Tcl's preprocessor voodoo for reuse by other packages. It's simpler, clearer, and safer for extensions to just define their own voodoo.


Category Porting - Category Windows