一个简单的tcl的dll扩展事例

复制内容到剪贴板
代码:
2008-02-24 09:11/*
* hello.cpp -- A minimal Tcl C extension.
*/
#include <tcl.h>
#include <windows.h>

BOOL APIENTRY
DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}

EXTERN_C static int
Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[])
{
Tcl_SetObjResult(interp, Tcl_NewStringObj("Hello, World!", -1));
return TCL_OK;
}

/*
* Hello_Init -- Called when Tcl loads your extension.
*/
EXTERN_C int DLLEXPORT
Hello_Init(Tcl_Interp *interp)
{
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
/* changed this to check for an error - GPS */
if (Tcl_PkgProvide(interp, "Hello", "1.0") == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, "hello", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
讲一下在VC中的编译过程以及在Tcl中的调用过程:
打开VC6之后,New->Projects->Win32 Dynamic-Link Library,创建完成之后,添加一个C++ source file.(VC中只能加CPP了,),将上面的代码添加其中,注意其中代码比原始的C 代码有所小改动,加了EXTERN_C高速编译器这是C 函数.Project->Settings->C/C++->Preprocessor definitions加入:USE_TCL_STUBS,并且在Project->Settings->Link中加入tclstub84.lib, tools->options->directorys->include files->D:/~~DEVP/TCL/TCL8.4.18/GENERIC(我的路径是这个,可能你的有所不同...就是找到你的tcl中的H 文件) ,tools->options->directorys->library files ->D:/~~DEVP/TCL/TCL8.4.18/WIN/RELEASE (这个是加入tcl8.4 lib文件,注意你的路径可能不同,就是你的tcl8.4.lib的地方) ,然后编译产生hello. dll.
下面是如何在tcl中调用这个扩展的hello命令了...创建一个文件夹,D:/cdcdevp/tcl/tcl8.4.18/library/hello,将hello.dll copy到这个文件夹中,在tclsh.exe中: 用这个命令:pkg_mkIndex $path hello.dll path是hello文件夹的路径,如果在路径已经是当前文件夹:pkg_mkIndex ./ hello.dll ,命令成功后产生 pkgIndex.tcl这个文件. 在tclsh 中输入package require Hello (注意Hello大写,来调用当前的 package), 然后输入hello 命令出现:Hello,World!

面的代码原自: http://wiki.tcl.tk/11153, 因为在VC中创建win32 dll project的时候,添加文件的时候没法引入C源文件,所以只好将其弄成C++,还好,还好,在原来的C的 基础上改动非常小~~~
下面对照原 文档翻译一下原来的东西:
在这个文件中,TCL访问DLL的入口点是Hello_Init,并且Hello_Init负责扩展到Tcl 解释器的连接.步骤如下:
first:调用Tcl_InitStubs(允许Hello_Init?调用Tcl C API的其他部分) 然后创建扩展的内容(这里使用Tcl_CreateObjCommand来注册Hello_Cmd的名字为"hello"), 再然后正是提供Hello package.
在Hello_Cmd中,我们不管参数,只是把结果设置为包含"Hello,World!"的字符串.参数"-1"意味着以C的strlen();一个正的数值则表示字符串的长度.
关于Hello_Cmd,当Hello_Cmd被调用的时候,objv array保存了指向Tcl_Obj结构的指针。
复制内容到剪贴板
代码:
At that point you could pass any of those pointers to Tcl_GetString() and get back a pointer to an array of bytes that are a string in Tcl's internal encoding terminated by a NULL byte. If you pass one of those pointers to some other routine, it could happen that the Tcl_Obj might get free'd by that routine. So to prevent that you should Tcl_IncrRefCount(objv) on any argument you need to be sure lives on after being passed to something and balance that with a Tcl_DecrRefCount(objv) when you don't need the insurance any more (Tcl_GetString will not free the Tcl_Obj). Just because the objv isn't freed, you can't conclude that the pointer you get back from Tcl_GetString() might not be freed. Their lifetimes are not the same. So, for any pointer you get back from Tcl_GetString(), you ought to make a copy of that string before you do any thing else, assuming you want to keep it around for a while (see also Tcl_Obj refCount HOWTO).===这一段看得不是太懂.不好翻译。
下面说一下Tcl_Obj, Tcl_Obj并非真正的OO概念中的Obj,或者说只是一种容器,这个容器包含了
复制内容到剪贴板
代码:
an integer refCount
representing the number of references to this Tcl_Obj
a char *bytes
being the string representation of the object (under the doctrine `everything's a string'). For a non-empty string, objv->bytes points to Tcl_Alloc()ated memory. For an empty string, objv->bytes points to a static char in the Tcl library that holds a NUL byte.
an integer length
being the length of the string representation
a pointer to a Tcl_ObjType
which contains the type's name, and pointers to functions implementing the four fundamental operations which all Tcl_Obj instances are expected to implement.
a union internalRep
which is used to store up to two pointers of information which is opaque to Tcl.
使用Stub Libraries.
使用Stub Libraries的原因在于,每次TCL版本发布可能会带来Tcl CAPI的变化,如果创建的Tcl 扩展库中使用了这些Tcl CAPI就必须重新编译扩展库. TCL中解决这个问题的方法:创建了2个库,一个main library(tcl84s.lib)和 一个stub library (tclstub84.lib). main library包含了所有的代码, 而stub library只是一个大的跳转表,包含了main library的各个函数的地址. TCL扩展库通过调用跳转表来调用Tcl的C API,
如果你的tcl扩展库要使用stubs,必须在编译的时候加以说明,还要在扩展的Init过程中加一个新的调用(Examplea_Init),TCL_USE_STUBS compile-time flag(在VC中,设置为Project->Settings->C/C++->Preprocessor definitions加入:USE_TCL_STUBS,并且在Project->Settings->Link中加入tclstub84.lib)把对Tcl CAPI的调用转向使用stub table. 而Tcl_initStubs 调用保证跳转表得以初始化.
Tcl_CreateObjCommand :在传过来的对应解释器中创建一个新的命令, 这个命令对应的名字为Hello, 命令对应的过程是Hello_Cmd, 当对应的Tcl命令hello被调用的时候,Tcl解释器会调用对应的过程. Protype: Tcl_CreateObjCommand(interp, cmdName, proc, clientData, delteProc), Tcl_Interp *interp , char *cmdname, Tcl_ObjCmdProc *proc, ClientData clientData(?), Tcl_CmdDeleteProc *deleteProc(在cmdName被从解释器删除之前调用的过程,如果为Null,则命令被解释器删除之前就不需要干什么.)
对应过程Hello_Cmd的参数和结果比和与Tcl_ObjCmdProc 类型相配,而Tcl_ObjCmdProc为:
复制内容到剪贴板
代码:
typedef int Tcl_ObjCmdProc(
    ClientData clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *CONST objv[]);
当Hello_Cmd被调用的时候,clientData 和interp参数是来自送给Tcl_CreateObjCommand的参数 clientData 和interp 参数的副本,典型而言,clientData指向一个应用相关的数据结构,这个数据结构描述了当Hello_Cmd 被调用的时候怎么做. Objc和objv描述了给Hello的参数, objc = 参数对象的数目(包括命令的名字). objv 是参数对象值(这里的参数对象是Tcl_Obj). Unlike argv[argv] used in a string-based command procedure, objv[objc] will not contain Null. 其中对objv的声明为CONST,表明不可以更改objv指针数组中的指针元素的值.(do not assign new pointer values to any element of the array). 然而,改变任何一个单独参数对象的内部代表的值是可以的,举例来说, 用户可以调用Tcl_GetIntFromObject on objv[2] 来获得这个对象的integer representation, 这个调用可能会改变objv[2]指向的对象的类型,但是不会改变objv[2]的指向.
扩展的命令过程,此例中是Hello_Cmd必须返回一个integer code: TCL_OK, TCL_ERROR, TCL_RETURN, TCL_BREAK, or TCL_CONTINUE.如果过程想返回一个非空的结果, 可以调用Tcl_SetObjResult来设置解释器的结果.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值