How to build a loadable tcl dll with visual studio (microsoft C compiler)?[crosspost in comp.lang.tc

Discussion in 'C++' started by Michael Reichenbach, Sep 19, 2007.

  1. Example one from http://www.tcl.tk/man/tcl8.4/TclCmd/load.htm:

    #include <tcl.h>
    #include <stdio.h>
    static int fooCmd(ClientData clientData,
    Tcl_Interp *interp, int objc, char * CONST objv[]) {
    printf("called with %d arguments\n", objc);
    return TCL_OK;
    }
    int Foo_Init(Tcl_Interp *interp) {
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
    return TCL_ERROR;
    }
    printf("creating foo command");
    Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);
    return TCL_OK;
    }

    My compile.bat (using the current MinGW gcc port):

    set djgpp=c:\MinGW\djgpp.env
    set path=%path%;c:\MinGW\bin
    gcc -I C:\Tcl\include -s -shared -o a.dll a.cpp C:\Tcl\bin\tcl84.dll
    pause

    First example doesn`t compile. There is an error.

    gcc -I C:\Tcl\include -s -shared -o
    a.dll a.cpp C:\Tcl\bin\tcl84.dll
    a.cpp: In function `int Foo_Init(Tcl_Interp*)':
    a.cpp:13: error: invalid conversion from `int (*)(void*, Tcl_Interp*,
    int, char*
    const*)' to `int (*)(void*, Tcl_Interp*, int, Tcl_Obj* const*)'
    a.cpp:13: error: initializing argument 3 of `Tcl_Command_*
    Tcl_CreateObjComman
    d(Tcl_Interp*, const char*, int (*)(void*, Tcl_Interp*, int, Tcl_Obj*
    const*), v
    oid*, void (*)(void*))'

    Example two:

    #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);
    }

    This compiled with same compile.bat without any errors or warnings.

    I wanted to compile the second example also with visual studio
    (microsoft C compiler) and I did add the c:\tcl\include and the
    c:\tcl\lib paths. But it doesn`t work, there is an errormessage:

    Error 1 error C2664: 'Tcl_CreateObjCommand' : cannot convert parameter 3
    from 'int (__cdecl *)(ClientData,Tcl_Interp *,int,char *const [])' to
    'Tcl_ObjCmdProc (__cdecl *)'

    How can I fix it?

    You also don`t need to answer this specific question. Any other example
    code which will compile in visual studio and which can be used later as
    a loadable tcl module would be much appreciated.
     
    Michael Reichenbach, Sep 19, 2007
    #1
    1. Advertising

  2. Michael Reichenbach

    Don Porter Guest

    Re: How to build a loadable tcl dll with visual studio (microsoftC compiler)? [crosspost in comp.lang.tcl and comp.lang.c++]

    Michael Reichenbach wrote:
    > static int fooCmd(ClientData clientData,
    > Tcl_Interp *interp, int objc, char * CONST objv[]) {


    With that prototype, your fooCmd routine is a
    Tcl_CmdProc. It's a command procedure that takes
    string arguments...

    > Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);


    ....but you are passing fooCmd to
    Tcl_CreateObjCommand() and Tcl_CreateObjCommand
    expects a Tcl_ObjCmdProc, and not a Tcl_CmdProc.
    The compiler is telling you this error.

    Either convert your fooCmd routine to be a
    Tcl_ObjCmdProc -- a command procedure that
    takes (Tcl_Obj *) arguments -- or call
    Tcl_CreateCommand() instead of
    Tcl_CreateObjCommand().

    DGP
     
    Don Porter, Sep 20, 2007
    #2
    1. Advertising

  3. Re: How to build a loadable tcl dll with visual studio (microsoftC compiler)? [crosspost in comp.lang.tcl and comp.lang.c++]

    Well, I got the example from here:
    http://www.tcl.tk/man/tcl8.4/TclCmd/load.htm#M8. 8.4 is the current
    stable tcl release and this is the official documentation. Did them use
    a used wrong commands inside the official documentation example for years?

    I think this problem could compiler based.

    Unfortunately what you wrote did not help me.

    If you are using in visual studio 7 (2005) or 8 (ocras 2008) yourself
    and get such an extension running, please post the code here.
     
    Michael Reichenbach, Sep 20, 2007
    #3
  4. Michael Reichenbach

    Eric Hassold Guest

    Re: How to build a loadable tcl dll with visual studio (microsoftC compiler)? [crosspost in comp.lang.tcl and comp.lang.c++]

    Hi,

    Michael Reichenbach wrote :
    > Well, I got the example from here:
    > http://www.tcl.tk/man/tcl8.4/TclCmd/load.htm#M8. 8.4 is the current
    > stable tcl release and this is the official documentation. Did them use
    > a used wrong commands inside the official documentation example for years?


    Definately, documentation is buggy, objv argument should have
    declaration "Tcl_Obj *CONST objv[]", not char*. Error is still there in
    8.4.16-rc1 doc. This may deserve a quick bug report and fix before
    8.4.16 gets out.

    >
    > I think this problem could compiler based.
    >
    > Unfortunately what you wrote did not help me.


    You only need to fix fooCmd declaration:

    static int fooCmd(ClientData clientData,
    Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[]) {
    printf("called with %d arguments\n", objc);
    return TCL_OK;
    }

    and it will compile fine with mingw, cygwin, vs2k5 or any other
    compiler. I suggests you also define USE_TCL_STUBS=1
    (-DUSE_TCL_STUBS=1), and link with tclstub84.lib instead of tcl84.dll.
    This will make your extension more portable across versions and build
    environments.

    >
    > If you are using in visual studio 7 (2005) or 8 (ocras 2008) yourself
    > and get such an extension running, please post the code here.



    Eric
     
    Eric Hassold, Sep 20, 2007
    #4
  5. Re: How to build a loadable tcl dll with visual studio (microsoftC compiler)? [crosspost in comp.lang.tcl and comp.lang.c++]

    I wouldn`t say that I have understood everything. But with some magic,
    putting some hints together it`s working now. Was really hard for me as
    C++ beginner to get this working, because there was nowhere a working
    sample on the web. I have written some step by step instructions, if
    someone is interested in solving that problem, see here.

    1. File -> new project -> name: Foo -> win32 console application -> dll

    2. Tools -> Options -> VC++ Directorys -> Include Files -> added
    C:\Tcl\include

    3. C++ -> precompiled headers -> Not Using Precompiled Headers

    4. Library files -> C:\Tcl\lib

    5. Project settings -> Configuration Properties -> Linker -> Input ->
    Additional Dependencies -> added c:\tcl\lib\tclstub84.lib

    6. use this source (my mistake was to use the #define after #include)
    (without the extern "C" it`s also not working, you will get error
    message while % load Foo.dll, it says can`t find Foo_Init procedure)

    #define USE_TCL_STUBS 1

    #include <tcl.h>
    #include <stdio.h>

    extern "C"
    {
    __declspec(dllexport) int Foo_Init(Tcl_Interp* interp);
    }

    static int fooCmd(ClientData clientData, Tcl_Interp *interp, int objc,
    Tcl_Obj * CONST objv[]) {
    printf("called with %d arguments\n", objc);
    return TCL_OK;
    }

    int Foo_Init(Tcl_Interp *interp) {
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
    return TCL_ERROR;
    }
    printf("creating foo command");
    Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);
    return TCL_OK;
    }

    7. Debug -> change to Release

    8. Build -> Build solution -> compiles without any errors -> in project
    folder under release is File Foo.dll with ~6 kb
     
    Michael Reichenbach, Sep 20, 2007
    #5
  6. Michael Reichenbach

    grahamaaron

    Joined:
    Feb 8, 2010
    Messages:
    1
    DLL wrapper of C++/TCL crashes when release build

    Hi,
    If you are interfacing TCL and C++ and vice-versa in a multi-threaded windows application, you may find my application of interest….

    Thanks to the good work done by Maciej Sobczak in creating his C++/TCL library, I have managed to do wonders with my MFC application and its usage of an TCL interpreter instance. However when I recently tried to build my solution for release, I found my application crashes.

    My development goal is to get code written in TCL to utilise an SSH handler that exists as an object in my MFC application. The SSH handler is used to connect to Cisco Devices via a Secure Shell connection to allow my TCL scripts to issue and monitor Cisco CLI (command line interface) commands.

    Firstly I will explain what I have going (in debug at least):
    1) An MFC C++ application that creates multiple threads (for multiple SSH connections) each with its own interpreter object.
    2) To provide TCL access to the other C++ objects within each thread, I created a Function Wrapper DLL that is loaded via a TCL "load" command. This DLL contains a C++ class that is exposed to the world of TCL via the following:
    extern "C"
    {
    __declspec(dllexport) int Main_Init(Tcl_Interp* interp);
    }
    and the macro :

    CPPTCL_MODULE(Main, i)
    {
    i.class_<TCL_FunctionWrapper>("TCL_FunctionWrapper")
    .def("writeData", &TCL_FunctionWrapper::writeOnly)
    .def("readData", &TCL_FunctionWrapper::writeAndRead)
    .def("SetSSHConnectionHandler", &TCL_FunctionWrapper::SetSSHConnectionHandler);
    }
    where writeData, readData and SetSSHConnectionHandler are C++ methods of the class exposed by the DLL.
    The SetSSHConnectionHandler takes a string version of the C++ pointer to an object that handles SSH connection as follows:

    void TCL_FunctionWrapper::SetSSHConnectionHandler(string pHandler)
    {
    m_pSSHHandler = (CSSH_ConnectionHandler *) (long) atoi(pHandler.c_str());
    }

    This allows me to inform the DLL which pointer to use to when TCL calls writeData and readData methods.

    3) After performing the TCL statement "load C:\functionwrapper.dll Main", (where “Main” is as exposed by the CPPTCL_MODULE(Main, i) ) an instance of the TCL_FunctionWrapper object is created using:
    set FW [TCL_FunctionWrapper]

    After this point the SetSSHConnectionHandler can be called to initialise the FunctionWrapper with the pointer to the particular thread’s SSH handler (as mentioned above).

    As I said above, the whole application/arrangement works perfectly when both the application and the FunctionWrapper DLL are built for debug, but when the I build for release, the
    “load C:\functionwrapper.dll Main” causes the application to exit. I can see from my VS 2008 IDE output window that the load is performed but it is soon followed by the application thread and application exiting:
    'MyApplication.exe': Loaded 'C:\FunctionWrapper.dll', Symbols loaded.
    The program '[3672] MyApplication.exe: Native' has exited with code 0 (0x0).

    Another interesting point is that if I use a release version of the MFC application and a debug build of the DLL it works perfectly also. It only seems to fail when I build the FunctionWrapper DLL for release.

    I have tried stepping through my Release build (which some of you may not realise you can do, but it is possible) and I could see the last statement executed was the
    Tcl_Eval(interp_, script.c_str());
    which is used to invoke the “load” TCL command.

    Any thoughts would be very much appreciated.
    Thanks in advance.
    Aaron
     
    grahamaaron, Feb 8, 2010
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.

Share This Page