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

  • Thread starter Michael Reichenbach
  • Start date

M

Michael Reichenbach

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.
 
Ad

Advertisements

D

Don Porter

Michael said:
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
 
M

Michael Reichenbach

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.
 
E

Eric Hassold

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
 
M

Michael Reichenbach

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
 
Ad

Advertisements

Joined
Feb 8, 2010
Messages
1
Reaction score
0
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
 
Ad

Advertisements


Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top