Updated 2012-09-12 19:43:07 by dkf

It is possible to link a tclkit as a shared library or DLL. This is useful for use in applications that embed Tcl as the shared library contains a virtual filesystem containing the necessary script files. This means that the embedding of Tcl now only requires a single file to be distributed.

Notes on building a stardll are at http://www.patthoyts.tk/tclkit/building-basekits.html

ActiveState refer to tclkit and stardll as basekit and there is such a DLL included with ActiveTcl (base-tcl-thread-win32-ix86.dll on Windows).

TclKit_SetKitPath

The tclkit initialization code expects to mount the executable to access the tclkit virtual filesystem. When you use tclkit as a shared library you must inform the initialization code that it must mount a different file. Therefor the very first call you make must be to TclKit_SetKitPath(const char *path) giving the filename and path of the shared library. Second must be a call to TclKit_AppInit(Tcl_Interp *interp) to initialize the runtime.

Example

An example of an application that embeds tcl using this technique follows.
 /* tclembed.c - Copyright (C) 2003 Pat Thoyts <patthoyts@users.sourceforge.net>
  *
  * Sample of an embedded Tcl application linked using the Tcl stubs mechanism
  * as seen at http://wiki.tcl.tk/2074.
  *
  * This version demonstrates linking to the stardll (ActiveTcl calls it a
  * basekit DLL) which is a TclKit presented as a DLL.
  *
  * ----------------------------------------------------------------------
  * This source code is public domain.
  * ----------------------------------------------------------------------
  *
  * $Id$
  */
 
 #define STRICT
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 #ifndef USE_TCL_STUBS
 #define USE_TCL_STUBS
 #endif
 #include <tcl.h>
 
 static Tcl_Interp *InitializeTcl(const char *dllname,int argc, char *args[]);
 
 int
 main(int argc, char *argv[])
 {
     Tcl_Interp *interp;
     int r = TCL_OK;
     
     interp = InitializeTcl("basekit.dll", argc, argv);
     if (interp == NULL) {
         fprintf(stderr, "error: failed to initialize Tcl runtime\n");
     } else {
         if (argc > 1) {
             r = Tcl_EvalFile(interp, argv[1]);
             printf(Tcl_GetStringResult(interp));
         }
         Tcl_DeleteInterp(interp);
     }
     
     return r;
 }
 
 typedef Tcl_Interp * (*LPFNCREATEINTERP) ();
 typedef int          (*LPFNBASEKITINIT)  (Tcl_Interp *);
 typedef char *       (*LPFNSETKITPATH)   (char *);
 
 Tcl_Interp *
 InitializeTcl(const char *dllname, int argc, char *argv[])
 {
     TCHAR szLibrary[MAX_PATH];
     HINSTANCE hTcl = NULL;
     Tcl_Interp *interp = NULL;
     
     hTcl = LoadLibraryA(dllname);
     if (hTcl != NULL) {
 
         LPFNCREATEINTERP lpfnCreateInterp
             = (LPFNCREATEINTERP)GetProcAddress(hTcl, "Tcl_CreateInterp");
         LPFNBASEKITINIT lpfnBasekitInit 
             = (LPFNBASEKITINIT)GetProcAddress(hTcl, "TclKit_AppInit");
         LPFNSETKITPATH lpfnSetKitPath 
             = (LPFNSETKITPATH)GetProcAddress(hTcl, "TclKit_SetKitPath");
 
         if (lpfnCreateInterp != NULL && lpfnBasekitInit != NULL 
             && lpfnSetKitPath != NULL) 
         {
             interp = lpfnCreateInterp();
             if (interp != NULL) {
                 Tcl_InitStubs(interp, "8.4", 0);
                 Tcl_FindExecutable(argv[0]);
                 GetModuleFileNameA(hTcl, szLibrary, MAX_PATH);
                 lpfnSetKitPath(szLibrary);
                 if (lpfnBasekitInit(interp) == TCL_OK) {
                     Tcl_InitMemory(interp);
                 } else {
                     fprintf(stderr, "TclKit_AppInit: %s\n",
                             Tcl_GetStringResult(interp));
                 }
             }
         }
     }
     return interp;
 }