Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question about loading a DLL

67 views
Skip to first unread message

mike.h...@gmail.com

unread,
Aug 15, 2007, 5:14:20 PM8/15/07
to
Hi There,
I got a question about loading a DLL(dynamic link library) in TCL, I
created a simple DLL in VC++ 2005, the name of the file is "test.dll".
I put this file in the folder :" c:\Tcl\lib". However, when I try to
use the following command to load this dll,

%load test.dll

I got error message: "can not open test.dll libary, can not be found
in library path"
So should I put the dll in a specific folder? or it is the problem of
the dll file itself?

Thanks,

Mike

Ron Fox

unread,
Aug 15, 2007, 5:49:55 PM8/15/07
to
How about
load c:/Tcl/lib/test.dll

RF

Michael Schlenker

unread,
Aug 15, 2007, 6:00:45 PM8/15/07
to
mike.h...@gmail.com schrieb:

> Hi There,
> I got a question about loading a DLL(dynamic link library) in TCL, I
> created a simple DLL in VC++ 2005, the name of the file is "test.dll".
> I put this file in the folder :" c:\Tcl\lib". However, when I try to
> use the following command to load this dll,
>
> %load test.dll
>
> I got error message: "can not open test.dll libary, can not be found
> in library path"
> So should I put the dll in a specific folder?
Would work, same folder as the tclsh.exe or wish.exe you use is a simple
bet. Using an absolute path for the dll when using load should also work.

>or it is the problem of
> the dll file itself?

Might be if you don't have an appropriate Tcl init function in your dll
that is exported (__declspec(dllexport) Init_test(...) ), but that would
be a different error message IIRC. You cannot really load arbitrary dlls
with the Tcl load command, it expects an Init function.

Take a look at the sampleextension in cvs
(http://wiki.tcl.tk/sampleextension)
to see whats needed. (it uses autoconf though, even if it can build with
VC when passed the right flags).

Michael

mike.h...@gmail.com

unread,
Aug 15, 2007, 7:01:50 PM8/15/07
to
On Aug 15, 3:00 pm, Michael Schlenker <schl...@uni-oldenburg.de>
wrote:
> mike.hfzh...@gmail.com schrieb:> Hi There,

Hi there,
I tried :
load c:\tcl\lib\test.dll

it seems to find the dll file but output a message:
"couldn't find procedure Test_Init"

So I think you guys are right, then how can I create the procedure
Test_Init in my VC++ 2005 code?


Mike

suchenwi

unread,
Aug 16, 2007, 4:23:20 AM8/16/07
to
On 16 Aug., 01:01, mike.hfzh...@gmail.com wrote:
> So I think you guys are right, then how can I create the procedure
> Test_Init in my VC++ 2005 code?

See http://wiki.tcl.tk/15026 for a Windows DLL example. Basically,
your Test_Init needs a signature like

EXTERN_C int DECLSPEC_EXPORT Test_Init(Tcl_Interp* interp);

and call Tcl_CreateObjCommand() for each command you want to expose,
plus init stubs, declare that a package is provided, etc.

pascal

unread,
Aug 16, 2007, 4:27:37 AM8/16/07
to
The Test_Init function is like the main() function of a C program : an
entry point you have to provide.
int
Test_Init (Tcl_Interp * ti) {
// export your C commands callable from Tcl

Ron Fox

unread,
Aug 16, 2007, 6:43:58 AM8/16/07
to
Then when all is said and done, you can also expose a pacakge
in the dll (Tcl_PkgProvide), create an pkgIndex.tcl file that
tells the package loader how to find/load the package, and
then put all of that in say c:\Tcl\lib\yourpackagename

Then you can load/initialize the dll via:

package require yourpackagename

This is probably all preferable to the load command, and
will allow you to ditch the absolute pathing. So in summary:
- load has no path to search
- load and the package load system (uses load) all require
an initialization function for the dll.
Below is an extract from the load command man page describing this;
(apologies about the formatting).

Once the file has been loaded into the applicationâs address
space, one of two
initialization procedures will be invoked in the new code.
Typically the iniâ[m
tialization procedure will add new commands to a Tcl
interpreter. The name of
the initialization procedure is determined by packageName and
whether or not
the target interpreter is a safe one. For normal interpreters
the name of the
initialization procedure will have the form pkg_Init, where pkg
is the same as
packageName except that the first letter is converted to
upper case and all
other letters are converted to lower case. For example, if
packageName is foo
or FOo, the initialization procedureâs name will be Foo_Init.

If the target interpreter is a safe interpreter, then the name
of the initialâ[m
ization procedure will be pkg_SafeInit instead of pkg_Init. The
pkg_SafeInit
function should be written carefully, so that it initializes
the safe interâ[m
preter only with partial functionality provided by the package
that is safe
for use by untrusted code. For more information on Safe-Tcl, see
the safe manâ[m
ual entry.

The initialization procedure must match the following prototype:
typedef int Tcl_PackageInitProc(Tcl_Interp *interp);

- See the man page for pkg_mkIndex for information about making indices
and generally useful information about packaging. This should be in
help files for ActiveTcl if that's what you are using.


RF

mike.h...@gmail.com

unread,
Aug 16, 2007, 1:26:11 PM8/16/07
to
I have read the sample in http://wiki.tcl.tk/15026 and i recreated my
source file, but when I build the solution in VC, I got error
message:
"error C2065: 'Tcl_Interp' : undeclared identifier
error C2065: 'interp' : undeclared identifier
error C2448: 'Testdll_Init' : function-style initializer appears to be
a function definition"

So are there anything I need to modify in the code? Thanks,

Mike

My code is as following:

% Header file
// MathFuncsDll.h

namespace MathFuncs
{
class MyMathFuncs
{
public:
// Returns a + b
static __declspec(dllexport) double Add(double a, double b);

// Returns a - b
static __declspec(dllexport) double Subtract(double a, double
b);

// Returns a * b
static __declspec(dllexport) double Multiply(double a, double
b);

// Returns a / b
// Throws DivideByZeroException if b is 0
static __declspec(dllexport) double Divide(double a, double
b);
};
}

%source file

// MathFuncsDll.cpp
// compile with: /EHsc /LD

#include "MathFuncsDll.h"

#include <stdexcept>

using namespace std;

namespace MathFuncs
{
double MyMathFuncs::Add(double a, double b)
{
return a + b;
}

double MyMathFuncs::Subtract(double a, double b)
{
return a - b;
}

double MyMathFuncs::Multiply(double a, double b)
{
return a * b;
}

double MyMathFuncs::Divide(double a, double b)
{
if (b == 0)
{
throw new invalid_argument("b cannot be zero!");
}

return a / b;
}
}
extern "C" __declspec(dllexport)
Test_Init(Tcl_Interp* interp) {
#ifdef USE_TCL_STUBS
Tcl_InitStubs(interp, "8.4", 0);
#endif
Tcl_Obj *version = Tcl_SetVar2Ex(interp, "tclusb_version", NULL,
Tcl_NewDoubleObj(0.1),
TCL_LEAVE_ERR_MSG);
if (version == NULL)
return TCL_ERROR;

// Call Tcl_CreateObjCommand etc. Must explicitly cast each 'Cmd'
function to link successfully
Tcl_CreateCommand(interp, "Add", (Tcl_CmdProc *) AddCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

Tcl_CreateCommand(interp, "Subtract", (Tcl_CmdProc *) SubstractCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

Tcl_CreateCommand(interp, "Multiply", (Tcl_CmdProc *) MultiplyCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

Tcl_CreateCommand(interp, "Divide", (Tcl_CmdProc *) DivideCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);


//return TCL_OK;
//return r;
}


Ron Fox

unread,
Aug 16, 2007, 1:44:02 PM8/16/07
to
You probably need to
#include <tcl.h>

mike.h...@gmail.com

unread,
Aug 16, 2007, 2:09:32 PM8/16/07
to
On Aug 16, 10:44 am, Ron Fox <f...@nscl.msu.edu> wrote:
> You probably need to
> #include <tcl.h>

I have added it in the source file, I also put tcl.h in the hearder
files, but when I build the solution, i got "fatal error C1083: Cannot
open include file: 'tcl.h': No such file or directory"

Mike

sp...@controlq.com

unread,
Aug 16, 2007, 3:30:14 PM8/16/07
to
On Thu, 16 Aug 2007, mike.h...@gmail.com wrote:

> Date: Thu, 16 Aug 2007 18:09:32 -0000
> From: mike.h...@gmail.com
> Newsgroups: comp.lang.tcl
> Subject: Re: Question about loading a DLL

#include <tcl.h>

note the <> brackets imply that the include file will be found in
"all the usual places". On BSD, one might have to add a
-I/usr/local/include option on the gcc line to specify a "local extension"
include directory be searched as well as "all the usual places". Your
mileage (system) may vary. (-I/opt/include, -I/usr/pkg/include, and
various other incantations).

mike.h...@gmail.com

unread,
Aug 16, 2007, 3:36:51 PM8/16/07
to
On Aug 16, 12:30 pm, s...@controlq.com wrote:

> On Thu, 16 Aug 2007, mike.hfzh...@gmail.com wrote:
> > Date: Thu, 16 Aug 2007 18:09:32 -0000
> > From: mike.hfzh...@gmail.com

> > Newsgroups: comp.lang.tcl
> > Subject: Re: Question about loading a DLL
>
> > On Aug 16, 10:44 am, Ron Fox <f...@nscl.msu.edu> wrote:
> >> You probably need to
> >> #include <tcl.h>
>
> > I have added it in the source file, I also put tcl.h in the hearder
> > files, but when I build the solution, i got "fatal error C1083: Cannot
> > open include file: 'tcl.h': No such file or directory"
>
> > Mike
>
> #include <tcl.h>
>
> note the <> brackets imply that the include file will be found in
> "all the usual places". On BSD, one might have to add a
> -I/usr/local/include option on the gcc line to specify a "local extension"
> include directory be searched as well as "all the usual places". Your
> mileage (system) may vary. (-I/opt/include, -I/usr/pkg/include, and
> various other incantations).

can you explain what's BSD, GCC? where should I put" -I/usr/local/
include"
Thanks,

Mike

Ron Fox

unread,
Aug 16, 2007, 3:41:39 PM8/16/07
to
For Visual C/C++ you need to add the
C:\Tcl\include directory to the set of includes searched
for by the project at compile time. I think this is done by
right-clicking the project and selecting properties... and the
pre-processor drill down has that if memory (hazy) serves.

RF

mike.h...@gmail.com

unread,
Aug 16, 2007, 4:42:35 PM8/16/07
to

I have changed it to: #include "c:\Tcl\include\tcl.h", now it works,
but i still get two error messages:error C4430: missing type specifier
- int assumed. Note: C++ does not support default-int", error C2065:
'AddCmd' : undeclared identifier"
I already simplified my code as following, could you please let me
know the possible reason for that? thanks,

Mike

%Header file
// MathFuncsDll.h

namespace MathFuncs
{
class MyMathFuncs
{
public:
// Returns a + b
static __declspec(dllexport) double Add(double a, double b);

};
}
%Source file


// MathFuncsDll.cpp
// compile with: /EHsc /LD

#include "MathFuncsDll.h"
#include "c:\tcl\include\tcl.h"
#include <stdexcept>

using namespace std;

namespace MathFuncs
{
double MyMathFuncs::Add(double a, double b)
{
return a + b;
}
}

extern "C" __declspec(dllexport)

Test_Init(Tcl_Interp* interp) {
#ifdef USE_TCL_STUBS
Tcl_InitStubs(interp, "8.4", 0);
#endif
Tcl_Obj *version = Tcl_SetVar2Ex(interp, "tclusb_version", NULL,
Tcl_NewDoubleObj(0.1),
TCL_LEAVE_ERR_MSG);
if (version == NULL)
return TCL_ERROR;

int r = Tcl_PkgProvide(interp, "Tclusb", Tcl_GetString(version));


// Call Tcl_CreateObjCommand etc. Must explicitly cast each 'Cmd'
function to link successfully
Tcl_CreateCommand(interp, "Add", (Tcl_CmdProc *) AddCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);


//return TCL_OK;
return r;
}


sp...@controlq.com

unread,
Aug 16, 2007, 5:03:00 PM8/16/07
to
On Thu, 16 Aug 2007, mike.h...@gmail.com wrote:

> Date: Thu, 16 Aug 2007 19:36:51 -0000
> From: mike.h...@gmail.com

Mike,

The error message you reported indicates that you are probably not using
the Gnu C Compiler (gcc), but rather a different compiler (Visual C++?),
and also, likely on Windows, rather than BSD, so don't worry about 'BSD,
and GCC, but rather, where the header files for Tcl are installed on your
particular platform, and I'll almost guarantee that it is not /usr/local
unless you are using MSYS/MingW (though I strongly suspect that you are
not).

Compiling your code from scratch, you need to tell your C compiler where
to find the #include files, and this is almost assuredly an option in your
IDE, if you are using one, if not, perhaps you should be. I gave generic
help which while it does apply to your platform the specifics are
different, and it does imply some knowledge of the source compile/link on
your platform.

Going back to one of your previous posts, which included a section of the
C++ you are attempting to compile, it seems that the arithmetic routines
expect "double" args, but you have not extracted the arguments from the
TclObject types which will actually be passed, so even if this code
compiled, it would not execute as you expect, even if you were able to
compile it. You need to write a "WRAPPER" around that code if you want
it to work as expected.

sigh ... start with

http://wiki.tcl.tk/1191

and

http://www.equi4.com/pub/etc/extuse.html

I fear you have some work ahead ...
Good luck.

Rob Sciuk

mike.h...@gmail.com

unread,
Aug 16, 2007, 5:14:51 PM8/16/07
to
On Aug 16, 2:03 pm, s...@controlq.com wrote:
> On Thu, 16 Aug 2007, mike.hfzh...@gmail.com wrote:
> > Date: Thu, 16 Aug 2007 19:36:51 -0000
> Rob Sciuk- Hide quoted text -
>
> - Show quoted text -

You are right, the compiler I use is VC++ 2005, so do you mean if I
use Gnu C Compiler, it will make things a little bit easier?

Mike

Neil Madden

unread,
Aug 16, 2007, 5:22:47 PM8/16/07
to
mike.h...@gmail.com wrote:
> On Aug 16, 12:41 pm, Ron Fox <f...@nscl.msu.edu> wrote:
>> For Visual C/C++ you need to add the
>> C:\Tcl\include directory to the set of includes searched
>> for by the project at compile time. I think this is done by
>> right-clicking the project and selecting properties... and the
>> pre-processor drill down has that if memory (hazy) serves.
>>
>> RF
>
> I have changed it to: #include "c:\Tcl\include\tcl.h", now it works,

That might work, but you'd be better using just
#include <tcl.h>
and then modifying the project properties to add C:\Tcl\include to the
path searched for header/include files.

A simpler example of how to create a Tcl extension for Windows (and
other platforms) is at: http://wiki.tcl.tk/11153

> but i still get two error messages:error C4430: missing type specifier
> - int assumed. Note: C++ does not support default-int", error C2065:
> 'AddCmd' : undeclared identifier"

...


> extern "C" __declspec(dllexport)
>
> Test_Init(Tcl_Interp* interp) {

Your first error is here -- you haven't declared a return type for the
function (which should be 'int'):

extern "C" __declspec(dllexport)
int Test_Init(Tcl_Interp *interp) { ... }

...


> // Call Tcl_CreateObjCommand etc. Must explicitly cast each 'Cmd'
> function to link successfully
> Tcl_CreateCommand(interp, "Add", (Tcl_CmdProc *) AddCmd,
> (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);

You have no function named "AddCmd" in your source-code anywhere. You
need to write these functions to actually perform the command. I'd also
recommend using the newer Tcl_CreateObjCommand and friends. For example:

static int
AddCmd(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj * const objv[])
{
double a, b, c;
// Check arguments
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "a b");
return TCL_ERROR;
}
// Parse arguments
if (Tcl_GetDoubleFromObj(interp, objv[1], &a) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_GetDoubleFromObj(interp, objv[2], &b) != TCL_OK) {
return TCL_ERROR;
}
// Now perform our actual command
c = MathFuncs::MyMathFuncs::Add(a,b);
// Finally, return the result
Tcl_SetObjResult(interp, Tcl_NewDoubleObj(c));
return TCL_OK;
}

If you put that after your implementation of the MathFuncs namespace in
the source file, then you can change the Init function to be:

extern "C" __declspec(dllexport) int
Test_Init(Tcl_Interp *interp) {
Tcl_InitStubs(interp, "8.4", 0);
// Ignore package/version stuff for now...
Tcl_CreateObjCommand(interp, "Add", AddCmd, NULL, NULL);
return TCL_OK;
}

-- Neil

Neil Madden

unread,
Aug 16, 2007, 5:31:17 PM8/16/07
to
Neil Madden wrote:
...

> extern "C" __declspec(dllexport) int
> Test_Init(Tcl_Interp *interp) {
> Tcl_InitStubs(interp, "8.4", 0);
> // Ignore package/version stuff for now...
> Tcl_CreateObjCommand(interp, "Add", AddCmd, NULL, NULL);
> return TCL_OK;
> }

Ack... other things to mention (discussed on the wiki page I linked to:
http://wiki.tcl.tk/11153):

You should define USE_TCL_STUBS while building, and you should link
against the tcl stub library (called something like tclstub84.lib on
Windows, I think). These will both need to be setup in the project build
options (the first will be in settings for the preprocessor, while the
latter will be a setting for the linker). You may also need to tell the
linker where to find tclstub84.lib, which is probably C:\Tcl\lib.

-- Neil

sp...@controlq.com

unread,
Aug 16, 2007, 6:12:11 PM8/16/07
to
On Thu, 16 Aug 2007, mike.h...@gmail.com wrote:

> You are right, the compiler I use is VC++ 2005, so do you mean if I
> use Gnu C Compiler, it will make things a little bit easier?
>
> Mike

Um, no, it doesn't really matter which C compiler you use, the process
remains the same, but the details are different, that's all. I understand
that Tcl/TK uses the VC++ compiler to compile/link the official windows
downloads, and there are files (like a makefile for nmake) in the tcl
source tree which may be of some value to you.

Your first problem is to understand how to to properly write an extension,
and correctly pass arguments from the Tcl_Object interface to to your
C/C++ library. This is better explained in the wiki, and I believe others
on this thread have directed you to some good code examples, and some
good references on the topic. This boils down to converting Tcl_Objects
to the type (double?) your library arguments require, and returning
results in a similar (reversed) manner.

Next, in order to successfully compile you will need to understand how
your compiler finds its "include" files, (eg: locate the tcl.h header).
Finally, after your code compiles cleanly, you will need to then need to
understand how to link in any additional object files which need to be
resolved. These would include your own library code, the wrapper code,
TclStubs/or TclLibrary code, possibly some windows libraries, etc.

Linking is a bit of a black art as well, and various object files may be
needed (.OBJ, .o, .DLL, .so, .a or .lib are all possibilities). This can
be the most difficult aspect of the entire process, and this is also
where the Tcl makefiles (and more specifically the Tcl Extension
Architecture or TEA) may be of help, but that adds another layer of
complexity which I doubt you need just now.

Finally, when you have written proper tcl wrapper code and correctly
compiled your library and wrapper, you will go through a link stage which
will create the target .dll which contains all the object code necessary
to load and execute the extension. I certainly wish I could point you to
a single reference which documents the entire process, but I am unaware of
any. The Wiki is your friend here, but there is no substitute for a long
hard slog through online docs which are hard to find, and even harder to
understand.

Typically, in compiling C code for Windows, I cross compile from a FreeBSD
box, using a ported version of MingW32. This simplifies my life, as I
prefer a Unix based development system, but I doubt you'd gain the same
mileage as the learning curve is even longer than the one you are
embarking upon. Do I sound discouraging? Sorry, but I don't want to
sugar coat the fact that coding for Windows at the C level is tough, and
unnecessarily complicated.

I strongly suggest that you start with Ralph Fassel's SIMPLEST POSSIBLE
EXTENSION at the wiki link:

http://wiki.tcl.tk/1191

Get that to compile and link, and you are PRACTICALLY there. While it
does not handle the parameter passing issue, it is a complete example of
code which will, when loaded, add a new command, and do something when
invoked. When you understand how to do this with VC++ 5.x, *MUCH* will be
revealed.

HTH,
Rob Sciuk

mike.h...@gmail.com

unread,
Aug 17, 2007, 12:18:27 PM8/17/07
to
On Aug 16, 3:12 pm, s...@controlq.com wrote:

Hi Rob,
I appreciate it so much for kindly assistance, i understand you spend
your precious time to write these explanations in details, bear with
me for these simple questions, as my background is solid mechanics,
new to TCL and VC programming, but i have passion to learn it. I will
go through all your directions to see what I can find.

Mike

0 new messages