• What is the correct way to initialize Tcl from a C program?

    From Helmut Giese@21:1/5 to All on Thu Jan 12 17:57:01 2023
    Hello out there,
    I used to build and use DLLs regularly - about 15 to 20 years ago, and
    now I am not quite sure how to do it. I create a function 'initTcl'
    which is called by 'main' like this
    error = initTcl(argv[0]);
    practically immediately. It looks like this:
    ---
    int initTcl(char* argv0) {
    TclpSetInitialEncodings();
    Tcl_FindExecutable(argv0);
    ourInterp = Tcl_CreateInterp();
    if (Tcl_Init(ourInterp) != TCL_OK) {
    return TCL_ERROR;
    }
    return TCL_OK;
    }
    ---
    'argv0' is well the name of my program. It fails in 'FindExecutable'
    with the error
    'Exception thrown at 0x0000000000000000 in StmtParserExe.exe'

    Q: What is wrong in 'initTcl'?
    I am on Windows using Visual Studio and a .lib file and a 64 bit DLL
    from Ashok.

    Any help will be greatly appreciated
    Helmut

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Jan 12 19:13:30 2023
    * Helmut Giese <hgiese@ratiosoft.com>
    | Hello out there,
    | I used to build and use DLLs regularly - about 15 to 20 years ago, and
    | now I am not quite sure how to do it. I create a function 'initTcl'
    | which is called by 'main' like this
    | error = initTcl(argv[0]);
    | practically immediately. It looks like this:
    | ---
    | int initTcl(char* argv0) {
    | TclpSetInitialEncodings();
    | Tcl_FindExecutable(argv0);

    The manpage of Tcl_FindExecutable says:

    On UNIX platforms this procedure is typically invoked as the very first
    thing in the application's main program; it must be passed argv[0] as
    its argument.
    --<snip-snip>--
    On Windows platforms this procedure is typically invoked as the very
    first thing in the application's main program as well;


    Looking at the function body of Tcl_FindExecutable, we see that it calls TclpSetInitialEncodings() itself, but only after calling TclInitSubsystems():

    tcl8.6.12/generic/tclEncoding.c

    void
    Tcl_FindExecutable(
    const char *argv0) /* The value of the application's argv[0]
    * (native). */
    {
    TclInitSubsystems();
    TclpSetInitialEncodings();
    TclpFindExecutable(argv0);
    }

    So just do as the manpage says, get rid of the TclpSetInitialEncodings()
    and only call Tcl_FindExecutable(argv0).

    HTH
    R'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Harald Oehlmann@21:1/5 to All on Thu Jan 12 18:54:58 2023
    Am 12.01.2023 um 17:57 schrieb Helmut Giese:
    Hello out there,
    I used to build and use DLLs regularly - about 15 to 20 years ago, and
    now I am not quite sure how to do it. I create a function 'initTcl'
    which is called by 'main' like this
    error = initTcl(argv[0]);
    practically immediately. It looks like this:
    ---
    int initTcl(char* argv0) {
    TclpSetInitialEncodings();
    Tcl_FindExecutable(argv0);
    ourInterp = Tcl_CreateInterp();
    if (Tcl_Init(ourInterp) != TCL_OK) {
    return TCL_ERROR;
    }
    return TCL_OK;
    }
    ---
    'argv0' is well the name of my program. It fails in 'FindExecutable'
    with the error
    'Exception thrown at 0x0000000000000000 in StmtParserExe.exe'

    Q: What is wrong in 'initTcl'?
    I am on Windows using Visual Studio and a .lib file and a 64 bit DLL
    from Ashok.

    Any help will be greatly appreciated
    Helmut

    Hi Helmut,

    Your stuff is ok. "TclpSetInitialEncodings();" is your function, right ?

    I don't see why it should crash there, sorry.

    https://wiki.tcl-lang.org/page/How+to+embed+Tcl+in+C+applications

    All ok.
    When I made dll embedding, I traced through Tcl_FindExecutable.
    So, a debug build may help...

    Sorry,
    Harald

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dave@21:1/5 to All on Thu Jan 12 11:51:28 2023
    Look at tclAppInit.c in either of the source distributions:
    win/tclAppInit.c or unix/tclAppInit.c


    --
    computerjock AT mail DOT com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Helmut Giese@21:1/5 to All on Thu Jan 12 22:11:46 2023
    Hallo Ralf,

    void
    Tcl_FindExecutable(
    const char *argv0) /* The value of the application's argv[0]
    * (native). */
    {
    TclInitSubsystems();
    TclpSetInitialEncodings();
    TclpFindExecutable(argv0);
    }

    So just do as the manpage says, get rid of the TclpSetInitialEncodings()
    and only call Tcl_FindExecutable(argv0).
    spot on, that was it. Many thanks to you and to Dave and Harald of
    course, too. Now my program crashes well into its main parts - but
    that's a different story :(
    Best regards
    Helmut

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Nicolas@21:1/5 to All on Thu Jan 12 21:56:39 2023
    Le jeudi 12 janvier 2023 à 22:11:53 UTC+1, Helmut Giese a écrit :
    Hallo Ralf,

    void
    Tcl_FindExecutable(
    const char *argv0) /* The value of the application's argv[0]
    * (native). */
    {
    TclInitSubsystems();
    TclpSetInitialEncodings();
    TclpFindExecutable(argv0);
    }

    So just do as the manpage says, get rid of the TclpSetInitialEncodings() >and only call Tcl_FindExecutable(argv0).
    spot on, that was it. Many thanks to you and to Dave and Harald of
    course, too. Now my program crashes well into its main parts - but
    that's a different story :(
    Best regards
    Helmut
    Hi, for what it worth (and maybe it can help) here's mine:

    int Tk_AppInit(Tcl_Interp *interp) {
    #ifdef WIN32
    wchar_t* tmp = NULL;
    #else
    char* tmp = NULL;
    #endif
    size_t nFP_Length;
    Tcl_DString ds4Argv;

    /*
    * Initialize packages
    */
    if (Tcl_Init(interp) == TCL_ERROR) {
    return TCL_ERROR;
    }
    if (Tk_Init(interp) == TCL_ERROR) {
    return TCL_ERROR;
    }


    /*
    this is the place to create the exit handler, I do it like that:
    Tcl_CreateExitHandler(dL_exitHandler, x);
    where x is a pointer of mine
    */

    Tcl_SetSystemEncoding(interp, "utf-8");

    /*many other stuff goes here...*/

    return TCL_OK;
    }

    #ifdef WIN32
    int wmain(int argc, wchar_t **argv) {
    /*
    use fflush(NULL) to reset everything in case of crashes
    */
    fflush(NULL);
    #else
    int main (int argc, char* argv[]) {
    #endif
    int i, noprefs;
    for (i = noprefs = 0; i < argc; i++) /* prescan ... */
    {
    #ifdef WIN32
    if (wcscmp(argv[i], L"") && i == 1) {
    #else
    if (strcmp(argv[i], "") && i == 1) {
    #endif
    user_prefbuf = user_initloadpreferences_file(argv[i]);
    argv[i] = user_prefbuf;
    }
    else if (i > 0) noprefs = 1;
    }

    #ifndef __MACOSX_CORE__
    TclZipfs_AppHook(&argc, &argv);
    #endif
    //mainLoop
    Tk_Main(argc, argv, Tk_AppInit);

    exit(EXIT_SUCCESS);
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Helmut Giese@21:1/5 to All on Fri Jan 13 19:38:43 2023
    Hello Nicolas,
    #ifdef WIN32
    int wmain(int argc, wchar_t **argv) {
    /*
    use fflush(NULL) to reset everything in case of crashes
    */
    fflush(NULL);
    #else
    int main (int argc, char* argv[]) {
    #endif
    <snip>
    //mainLoop
    Tk_Main(argc, argv, Tk_AppInit);

    exit(EXIT_SUCCESS);
    }
    calling Tcl_Main (or Tk_Main) is good if Tcl is the base of your
    program. From the doc (Tcl_Main.html):
    ---
    Tcl_Main can serve as the main program for Tcl-based shell
    applications. A “shell application” is a program like tclsh or wish
    that supports both interactive interpretation of Tcl and evaluation of
    a script ...
    ---
    My program, however, is a C program which occasionally needs to call
    Tcl procs, so I needed a different approach - and with Ralf's
    suggestion it worked.
    Best regards
    Helmut

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)