Version 28 of Building Tcl DLL's for Windows

Updated 2004-06-10 09:05:02

Using Microsoft Visual C++ 6

  • Create a new win32 dynamic link library project and call it something nice, like tcldemo. This is going to be our package name too. (Note: Creating an MFC DLL project may cause problems, as the MFC framework infrastructure can conflict with the declarations in example code. You'll know you've got a _declspec() problem if you get unresolved symbols like __imp_TclCreateObjCommand() -RWT)
  • Select an 'Empty DLL' on the next dialog.
  • Before you get going, make sure you have told Visual Studio about the Tcl headers and libraries. To do this, open Tools->Options->Directories and give the Tcl include directory in the includes box and the libraries in the libraries box.
  • Open up the Project settings menu item for your new project and go to the C++ tab. Add USE_TCL_STUBS to the list of defined symbols and in the Link tab prepend tclstub83.lib to the list of libraries. (Adust the numerical suffix for your version of tcl).
  • Now we can write some code. Create a new C++ source file, called tcldemo.cpp and paste in the code below.
  • Build it. Ignore the whining about MSCVRT conflicts. You now have a DLL in either Debug/Release.
  • Fire up tclsh or better tkcon from the Debug or Release subdirectory under your project and issue
  load tcldemo.dll Tcldemo
  set tcldemo_version
  package require Tcldemo
  • You should get 0.1 printed twice!

Using mingw32 GNU C / C++

If you don't happen to have a copy of Microsoft's compiler then you can get a copy of GNU C compiled for the win32 platform. The mingw32 project is the minimal approach which uses only the native libraries and so doesn't support unix porting much - see http://sourceforge.net/projects/mingw for the releases.

  • Create a .cpp or .cc file with the code below using your favourite editor. (eg: emacs)
  • Create a tcldemo.def file with the following lines:
 EXPORTS
 Tcldemo_Init
 Tcldemo_SafeInit
  • Compile the code:
 g++ -Wall -g -Ii:/opt/Tcl/include -c tcldemo.cpp -o tcldemo.o
 dllwrap --driver-name g++ -def tcldemo.def -o tcldemo.dll tcldemo.o -Li:/opt/Tcl/lib -ltcl83 -lm
  • Voila: you now have a tcldemo.dll. Test as above.

I've included a suitable Makefile below.

[ Q: isn't it possible to do this without a .def file? I thought the __declspec(dllexport) was supposed to make it unnecessary ]


Dana Robinson: You can use either. You can even have some functions declared as __declspec(dllexport) and other ones specified in a .def file and it'll all work out. Personally, I like to use .def files with platform-independent code. They aren't that hard to maintain and you don't have to mess up your pretty ANSI C/C++ code with a bunch of #ifdefs and MS-specific code.


The code:

 #include <windows.h>
 #include <tcl.h>

 #ifndef DECLSPEC_EXPORT
 #define DECLSPEC_EXPORT __declspec(dllexport)
 #endif // DECLSPEC_EXPORT

 BOOL APIENTRY
 DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
 {
     return TRUE;
 }

 EXTERN_C int DECLSPEC_EXPORT
 Tcldemo_Init(Tcl_Interp* interp)
 {
 #ifdef USE_TCL_STUBS
     Tcl_InitStubs(interp, "8.3", 0);
 #endif
     Tcl_Obj *version = Tcl_SetVar2Ex(interp, "tcldemo_version", NULL,
                                      Tcl_NewDoubleObj(0.1), TCL_LEAVE_ERR_MSG);
     if (version == NULL)
         return TCL_ERROR;
     int r = Tcl_PkgProvide(interp, "Tcldemo", Tcl_GetString(version));

     // Call Tcl_CreateObjCommand etc.

     return r;
 }

 EXTERN_C int DECLSPEC_EXPORT
 Tcldemo_SafeInit(Tcl_Interp* interp)
 {
     // We don't need to be specially safe so...
     return Tcldemo_Init(interp);
 }

 # -*- Makefile -*- for Tcl Demo
 #
 # @(#)$Id: 2419,v 1.29 2004-06-11 06:00:30 jcw Exp $

 CC      =g++
 DLLWRAP =dllwrap
 DLLTOOL =dlltool
 RM      =rm -f
 CFLAGS  =-Wall -Ii:/opt/tcl/include -DUSE_TCL_STUBS
 LDFLAGS =-Li:/opt/tcl/lib
 LIBS    =-ltclstub83

 DLL     =tcldemo.dll
 DEFFILE =tcldemo.def

 WRAPFLAGS =--driver-name $(CC) --def $(DEFFILE)

 CSRCS   =tcldemo.cpp
 OBJS    =$(CSRCS:.cpp=.o)

 $(DLL): $(OBJS)
        $(DLLWRAP) $(WRAPFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)

 clean:
        $(RM) *.o core *~

 %.o: %.cpp
        $(CC) $(CFLAGS) -c $< -o $@

 .PHONY: clean

 #
 # Local variables:
 #   mode: makefile
 # End:
 #

This excellent piece of work was created and put here by Pat Thoyts. Thanks Pat!


Paul Kienzle: With recent versions of mingw you can dispense with .def files, DECLSPEC_EXPORT, #include <windows.h>, DllMain, and dllwrap, and just use:

  g++ -Wall -g -Ii:/opt/Tcl/include -DUSE_TCL_STUBS -c tcldemo.cpp -o tcldemo.o
  g++ -shared -o tcldemo.dll tcldemo.o -Li:/opt/Tcl/lib -ltclstub83

After correcting the paths in my Makefile, my tcl extension from unix built and worked fine with ActiveState's binary distribution for windows.


I was unable to produce functional DLL with Paul Kienzles advice, but found another easy way to compile shared library with MinGW for MinGW-compiled Tcl:

 gcc -I C:\Tcl\include -s -shared -o test.dll test.c C:\Tcl\bin\tcl84.dll

Apparently new version of gcc can extract all required names from the DLL by itself, without using libtclXX.a or libtclstubXX.a. --mjk


See also Building Extensions on Windows.


Question: Once you have built the extensions, where should they be installed, and what gets installed?


Category Porting