DLLEXPORT and DLLIMPORT

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.