Note PT 25-Aug-2005: Some of this information is getting old and the page could do with an update.
There is a sampleextension package available from http://core.tcl.tk/sampleextension . This provides an example of how to write a Tcl extension and also shows how to setup the autoconf files and makefiles for use on unix. The win/ subdirectory has a fairly useful makefile for MSVC++ as well and I would recommend modifying this makefile.vc in preference to rolling your own.
Using Microsoft Visual C++ 6
load tcldemo.dll Tcldemo set tcldemo_version package require Tcldemo
cjl adds: I just spent a frustrating afternoon fighting an error message when trying to load a .dll created in this way on machines other than the build machine. Since googling revealed only others with similar problems, but no solutions, I thought I'd record it here.
Couldn't load library "whatever.dll" invalid argument
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 Building Tcl/Tk with Mingw for info about building Tcl/Tk with Msys and Mingw.
EXPORTS Tcldemo_Init Tcldemo_SafeInit
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
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.51 2006-01-28 07:00:31 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 newer versions of gcc can extract all required names from the DLL by itself, without using libtclXX.a or libtclstubXX.a. --mjk
( g++.exe can do that, too. But then use a extern "C" block for init and wrapper functions. You can then still call your other C++ functions and create objects, use new and delete etc. and pass the result back to the wrapper function in the extern "C" block. )
I also was able to make this work with Active Tcl 8.4, but you have to remember to remove "USE_TCL_STUBS" fromn your source code or compile option --Larry Rowe
See also Building Extensions on Windows.
Question: Once you have built the extensions, where should they be installed, and what gets installed?
Hello!
When compiling Tcldemo with MS Visual Studio .NET I got some errors:
tcldemo error LNK2019: unresolved external symbol __imp__Tcl_PkgProvide referenced in function _Tcldemo_Init tcldemo error LNK2019: unresolved external symbol __imp__Tcl_GetString referenced in function _Tcldemo_Init tcldemo error LNK2019: unresolved external symbol __imp__Tcl_NewDoubleObj referenced in function _Tcldemo_Init tcldemo error LNK2019: unresolved external symbol __imp__Tcl_SetVar2Ex referenced in function _Tcldemo_Init tcldemo fatal error LNK1120: 4 unresolved externals
How to correct these errors?
And after executing
gcc -I C:\Tcl\include -s -shared -o tcldemo.dll tcldemo.c C:\Tcl\bin\tcl84.dll
in Cygwin I could'n load tcldemo.dll, because of this error:
couldn't load library "tcldemo.dll": this library or a dependent library couldn't be found in library path when executing "load tcldemo.dll Tcldemo"
Why does it happen?
Thank you. Anton.
Peter Newman 22 March 2005: I can't help with the MS Visual Studio problems. But the couldnt load library "tcldemo.dll" should be easy to fix. It just means that tcldemo.dll isn't where wish or tclsh can find it. See the library and tclvars manpages - for details of where Tcl searches for things. You'll have to EITHER; copy tcldemo.dll to one of the directorys Tcl searches for loadable stuff in, OR; add the directory tcldemo.dll is in, to the appropriate Tcl variable. See the library and tclvars manpages - and in particular, variables like; tcl_library, auto_path, env(TCL_LIBRARY) and env(TCLLIBPATH).
Erik Leunissen 7 Jan 2006 In my experience, the error message "couldn't load library XXXX: this library or a dependent library couldn't be found in library path" is not as easy to fix as suggested above. The environment variables TCL_LIBRARY and TCLLIBPATH do not relate to the [load] command, but rather to [package require]. Therefore, their setting won't affect this problem. The problem is that the message doesn't say which library couldn't be found. The web page at:
http://flashexperiments.insh-allah.com/ApacheError126.html
gives advice about how to find out which library that is. Using the File Monitor tool mentioned in that page, I have been succesful in resolving such a problem.
In the Tcldemo example when I added:
Tcl_CreateObjCommand(interp, "fact",wrap_fact, (ClientData) NULL,
(Tcl_CmdDeleteProc *) NULL);
I get the following error:
--------------------Configuration: new_test - Win32 Debug-------------------- Compiling... tcldemo.cpp C:\new_test\tcldemo.cpp(45) : error C2664: 'struct Tcl_Command_ *(struct Tcl_Interp *,const char *,int (__cdecl *)(void *,struct Tcl_Interp *,int,struct Tcl_Obj *const * ),void *,void (__cdecl *)(void *))' : cannot convert parameter 3 from '' to 'int (__cdecl *)(void *,struct Tcl_Interp *,int,struct Tcl_Obj *const * )' None of the functions with this name in scope match the target type Error executing cl.exe. new_test.dll - 1 error(s), 0 warning(s)
Appreciate any insights/help. Thanks. RGM
PT This error is the compiler informing you that your function has the wrong type. Parameter 3 is the C function that will implement your fact command and should be a Tcl_ObjCmdProc type. Looks to me like you maybe forgot to declare the prototype? Something like this should do:
static Tcl_ObjCmdProc HelloObjCmdProc; int HelloObjCmdProc(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_SetObjResult(interp, Tcl_NewStringObj("hello, world", -1)); return TCL_OK; }
HaO 2017-01-30: When I linked the stubs library with a MS-VC2015 compiled C++ (actually CLR) 32 bit project, I got the link error for the release version: "error LNK2026: module unsafe for SAFESEH image". My "tclstub85.tcl" was compiled with MS-VC6.
Recompiling TCL as follows solved the issue: