'''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''' * 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! '''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. * The error message Couldn't load library "whatever.dll" invalid argument * The probable cause : Debug builds depend on debug versions of the language runtime, which isn't available for redistribution. * The solution : Only ship release builds to none-development machines. You may still need to install the appropriate "Redistributable package" for your version of Visual Studio, or package the relevant files with your own. Look in the documentation for "deployment" for the alternative methods. '''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 [Building Tcl/Tk with Mingw] for info about building Tcl/Tk with Msys and Mingw. * 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 * Link the code. If you are using a standard distribution such as ActiveTcl then see [How to create mingw32 libraries from DLLs]: 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 #include #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 , 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; } **Link error: LNK2026: module unsafe for SAFESEH image** [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: * In the start menu: Visual Studio 2015->Visual Studio Tools->Windows Desktop Command Prompts->VS2015 x86 Native Tools-Input prompt * Go to the win folder of the tcl source distribution: cd c:\test\tcl8.5.19\win * Start compilation: nmake -f Makefile.vc release OPTS=threads * Install the files: nmake -f makefile.vc install INSTALLDIR=c:\test\tcl8519_vc15 * Now use C:\test\tcl8519_vc15\lib\tclstub85.lib <> Windows | Foreign Interfaces | Porting