在脚本语言中使用COM对象 -- 第一部分 (Tcl/Tk)
本文演示了如何在Tcl/Tk中实例化一个COM对象并使用其方法和属性。
引言
我们通常在VBScript和JavaScript等脚本语言中使用COM对象。在本文中,我想向您展示如何在Tcl/Tk脚本语言中使用它。本文假设读者已对COM有深入的了解。本文也无法让您完全理解Tcl/Tk。期望读者通过其他可用资源来掌握Tcl/Tk。本文仅用于演示在Tcl/Tk中使用COM对象。在展示如何在Tcl/Tk中实例化COM组件并使用接口之前,我想先简要介绍一下TclTk本身。
背景
Tcl/Tk 是由加州大学伯克利分校的 John Ousterhout 开发的一个易于使用且具有非常有用图形界面功能的编程系统。Tcl 是基本编程语言,而 TK 是一个控件工具包,它提供了类似于 Windows 平台上的 Win32 SDK 或 MFC 以及 UNIX 平台上的 Motif 等其他 GUI 工具包的图形对象。TCL 代表工具命令语言,是一种解释型语言;Tk 代表工具包,是语言本身提供的扩展,用于操作控件,一旦掌握了 Tcl/Tk 系统,就可以非常快速地构建有用的应用程序。解释型语言的本质使其能够非常方便地通过 Tk 交互式地创建和测试 GUI 元素。
可以从 http://www.activestate.com 获取适用于 Windows、Linux、Solaris 和 Mac 等平台的 Tcl/Tk。
在 Windows 上,ActiveState 安装程序会为 Tcl 语言安装许多有用的扩展包,其中之一就是 COM 扩展。COM 的包名是 tcom。要使用此包,您必须在 Tcl 脚本开头使用命令 package require tcom
来加载它。在不深入 Tcl/Tk 细节(留给读者自行探索)的情况下,让我们快速了解一下如何交互式地创建一个按钮,为其分配一个命令,并在窗口中显示它。
启动 Tk wish 解释器 (Wish 是 Window Shell 的缩写)。注意:您也可以启动 Tcl shell,但如果没有通过命令 package require Tk.
加载 Tk 包,则无法直接发出控件命令。
wish 解释器启动后,会为您提供一个根窗口和一个命令 shell。

在命令 shell 中逐行输入以下内容,每行输入后按回车键。
button .b -text "Hello" -command hello
pack .b
button .q -text "Quit" -command exit
pack .q
proc hello { } {
puts "Hello Tk"
}
输入 pack .b
行后,按钮就会显示在根窗口中。
最后,在编写了 hello 命令的过程并单击 Hello 按钮后,它将在命令 shell 中打印字符串 Hello Tk。您还可以将上述行写在一个扩展名为 .tcl
的文件中。然后,在 Windows 资源管理器中双击该文件将启动 wish 解释器并运行应用程序。您也可以在解释器中使用命令 source <filename>
来运行脚本。
单击 Quit 按钮将退出解释器,从而退出窗口和 shell。
请注意,Quit 按钮在以下行中附加了系统命令 exit
:
button .q -text "Quit" -command exit
而 Hello 按钮附加了一个用户定义的在以下行中的过程:
button .b -text "Hello" -command hello
用户定义的过程由关键字 proc
定义。
proc hello { } {
puts "Hello Tk"
}
使用 tcom
在了解了 Tk 的功能之后,让我们继续看看如何实例化 COM 对象以及如何通过其实现的相应接口访问其功能。
在本文中,我们有一个名为 SimpleCOM
的 COM 库(源代码和 VS2008 项目在下载中提供),其中有一个名为 GraphicPoint
的对象。GraphicPoint
实现了三个接口,它们都派生自 IDispatch
以支持脚本...
IPoint
- 方法
SetCoord
GetCoord
Distance
- 属性
X
Y
Z
IGraphicPoint
- 方法
绘制
IColor
- 方法
SetColor
GetColor
(OLE_COLOR
)
我们将实例化 2 个 GraphicPoint
对象,获取它们的 IPoint
接口,并使用 SetCoord
方法设置 Points
的坐标。坐标将通过我们使用 Tk 开发的 GUI 从用户输入中获取。我们还将通过获取 IColor
接口来设置两个点的颜色。在这里,我们将看到如何将从颜色对话框中获得的 RGB 分量转换为 OLE_COLOR
。然后,我们将通过调用 IPoint
接口中的 Distance
方法来计算两个点之间的距离。我们还将通过弹出显示每个点坐标和颜色的消息框来模拟点的绘制。为此,我们将调用 IGraphicPoint
接口的 Draw
方法。当在 Tk 代码中实例化点时,我们还将弹出一个 messagebox
显示我们设置的坐标。为此,我们将调用 point
对象的 X
、Y
和 Z
属性。所有这些将涵盖实例化 COM 对象、查询相应接口以及使用方法和属性的活动。
加载 tcom 包所需的第一个命令是 package require tcom
。
以下代码展示了如何创建 GUI 元素 (控件) 以及按钮的回调过程。代码中的注释以 #
开头,并解释了创建 GUI、实例化组件及其使用所涉及的步骤。
# Create the widgets
# Container frames
frame .f1 -bd 5
frame .f2 -bd 5
frame .f3 -bd 5
pack .f1 -expand y -fill both
pack .f2 -expand y -fill both
pack .f3 -expand y -fill both
# Point 1
labelframe .f1.pt1frm -text "Point 1" -bd 2 -padx 3 -pady 3
pack .f1.pt1frm -side left -expand y -fill both
frame .f1.pt1frm.xfrm -bd 2
frame .f1.pt1frm.yfrm -bd 2
frame .f1.pt1frm.zfrm -bd 2
frame .f1.pt1frm.cfrm -bd 2
pack .f1.pt1frm.xfrm -expand y -fill both
pack .f1.pt1frm.yfrm -expand y -fill both
pack .f1.pt1frm.zfrm -expand y -fill both
pack .f1.pt1frm.cfrm -expand y -fill both
label .f1.pt1frm.xfrm.xlab -text "X" -justify left
label .f1.pt1frm.yfrm.ylab -text "Y" -justify left
label .f1.pt1frm.zfrm.zlab -text "Z" -justify left
pack .f1.pt1frm.xfrm.xlab -side left -expand y -fill both
pack .f1.pt1frm.yfrm.ylab -side left -expand y -fill both
pack .f1.pt1frm.zfrm.zlab -side left -expand y -fill both
entry .f1.pt1frm.xfrm.xent
.f1.pt1frm.xfrm.xent insert 0 "0.0"
entry .f1.pt1frm.yfrm.yent
.f1.pt1frm.yfrm.yent insert 0 "0.0"
entry .f1.pt1frm.zfrm.zent
.f1.pt1frm.zfrm.zent insert 0 "0.0"
pack .f1.pt1frm.xfrm.xent -side right -expand y -fill both
pack .f1.pt1frm.yfrm.yent -side right -expand y -fill both
pack .f1.pt1frm.zfrm.zent -side right -expand y -fill both
# Point 2
labelframe .f1.pt2frm -text "Point 2" -bd 2 -padx 3 -pady 3
pack .f1.pt2frm -side right -expand y -fill both
frame .f1.pt2frm.xfrm -bd 2
frame .f1.pt2frm.yfrm -bd 2
frame .f1.pt2frm.zfrm -bd 2
frame .f1.pt2frm.cfrm -bd 2
pack .f1.pt2frm.xfrm -expand y -fill both
pack .f1.pt2frm.yfrm -expand y -fill both
pack .f1.pt2frm.zfrm -expand y -fill both
pack .f1.pt2frm.cfrm -expand y -fill both
label .f1.pt2frm.xfrm.xlab -text "X" -justify left
label .f1.pt2frm.yfrm.ylab -text "Y" -justify left
label .f1.pt2frm.zfrm.zlab -text "Z" -justify left
pack .f1.pt2frm.xfrm.xlab -side left -expand y -fill both
pack .f1.pt2frm.yfrm.ylab -side left -expand y -fill both
pack .f1.pt2frm.zfrm.zlab -side left -expand y -fill both
entry .f1.pt2frm.xfrm.xent
.f1.pt2frm.xfrm.xent insert 0 "0.0"
entry .f1.pt2frm.yfrm.yent
.f1.pt2frm.yfrm.yent insert 0 "0.0"
entry .f1.pt2frm.zfrm.zent
.f1.pt2frm.zfrm.zent insert 0 "0.0"
pack .f1.pt2frm.xfrm.xent -side right -expand y -fill both
pack .f1.pt2frm.yfrm.yent -side right -expand y -fill both
pack .f1.pt2frm.zfrm.zent -side right -expand y -fill both
# Color button 1. The command onSelectColor1 receives the toplevel window
and the button as argument
button .f1.pt1frm.cfrm.btn -text "Choose Color"
-command "onSelectColor1 . .f1.pt1frm.cfrm.btn"
pack .f1.pt1frm.cfrm.btn -expand y -fill both
# Color button 2
button .f1.pt2frm.cfrm.btn -text "Choose Color"
-command "onSelectColor2 . .f1.pt2frm.cfrm.btn"
pack .f1.pt2frm.cfrm.btn -expand y -fill both
# Distance Result
label .f2.lab1 -text "Distance Between Point 1 and Point 2"
# Text Entry holding the distance result. Note the -textvariable option
# The ::res variable when set with a value updates the Entry
entry .f2.ent1 -state readonly -textvariable ::res
pack .f2.lab1 -side left -padx 2
pack .f2.ent1 -side right -expand y -fill both -padx 2
# Buttons
button .f3.apply -text "Apply" -width 10 -command onApply
button .f3.quit -text "Quit" -width 10 -command exit
pack .f3.quit -side right -fill y -padx 2
pack .f3.apply -side right -fill y -padx 2
# Globals to hold the Points colors
set ::g_Color1 [.f1.pt1frm.cfrm.btn cget -bg]
set ::g_Color2 [.f1.pt2frm.cfrm.btn cget -bg]
Apply 按钮被单击时会回调 onApply
过程,在该过程中我们将实例化 GraphicPoint
对象并使用它。
# Callbacks
proc onApply { } {
# rgbcolor is the list holding the RGB components of the global variable ::g_Color1
set rgbcolor [winfo rgb . $::g_Color1]
set hx 0xff
set r [lindex $rgbcolor 0]
set g [lindex $rgbcolor 1]
set b [lindex $rgbcolor 2]
# code for converting RGB values to UInteger (OLE_COLOR)
set color1 [expr ((($hx & $b) << 16) | (($hx & $g) << 8) | ($hx & $r))]
set rgbcolor [winfo rgb . $::g_Color2]
set hx 0xff
set r [lindex $rgbcolor 0]
set g [lindex $rgbcolor 1]
set b [lindex $rgbcolor 2]
# code for converting RGB values to UInteger (OLE_COLOR)
set color2 [expr ((($hx & $b) << 16) | (($hx & $g) << 8) | ($hx & $r))]
# Get typelib info from registry
# The UUID FA3BF2A2-7220-47ED-8F07-D154B65AA031 is the Library ID of SimpleCOM.
set SimpleCOM [registry get HKEY_CLASSES_ROOT\\TypeLib\\
{FA3BF2A2-7220-47ED-8F07-D154B65AA031}\\1.0\\0\\win32 {}]
::tcom::import $SimpleCOM
# This also directly works with dll in physical location
# ::tcom::import SimpleCOM.dll
# Prints all the available commands in the library
# Useful only in interactive shell
info commands SimpleCOMLib::*
# First Point CreateInstance
set aGrPoint [::SimpleCOMLib::GraphicPoint]
# QueryInterface for IPoint
set aPoint [::SimpleCOMLib::IPoint $aGrPoint]
# Call Interface method
$aPoint -method SetCoord [.f1.pt1frm.xfrm.xent get]
[.f1.pt1frm.yfrm.yent get] [.f1.pt1frm.zfrm.zent get]
# Call Interface get property
tk_messageBox -message "Point Created At X [$aPoint -get X],
Y [$aPoint -get Y], Z[$aPoint -get Z]" -title "From TkCOMGUI" -type ok -icon info
# QueryInterface for IColor
set aColor [::SimpleCOMLib::IColor $aGrPoint]
$aColor -method SetColor $color1
$aGrPoint -method Draw
# Second Point CreateInstance
set aGrPoint2 [::SimpleCOMLib::GraphicPoint]
# QueryInterface for IPoint
set aPoint2 [::SimpleCOMLib::IPoint $aGrPoint2]
# Call interface put properties
$aPoint2 -set X [.f1.pt2frm.xfrm.xent get]
$aPoint2 -set Y [.f1.pt2frm.yfrm.yent get]
$aPoint2 -set Z [.f1.pt2frm.zfrm.zent get]
# Call Interface get property
tk_messageBox -message "Point Created At X [$aPoint2 -get X],
Y [$aPoint2 -get Y], Z[$aPoint2 -get Z]" -title "From TkCOMGUI" -type ok -icon info
# QueryInterface for IColor
set aColor2 [::SimpleCOMLib::IColor $aGrPoint2]
$aColor2 -method SetColor $color2
$aGrPoint2 -method Draw
# Calculate distance between points by calling method Distance
set distVal [$aPoint -method Distance $aPoint2]
tk_messageBox -message "Distance between Points is $distVal"
-title "From TkCOMGUI" -type ok -icon info
# Update the Entry value
set ::res $distVal
update
}
proc onSelectColor1 {p b} {
# pop up the color chooser and store selection in global color variable
set ::g_Color1 [tk_chooseColor -title "Choose a color" -parent $p]
# set the button color
$b configure -bg $::g_Color1
update
}
proc onSelectColor2 {p b} {
# pop up the color chooser and store selection in global color variable
set ::g_Color2 [tk_chooseColor -title "Choose a color" -parent $p]
# set the button color
$b configure -bg $::g_Color2
update
}
颜色选择按钮的回调只是显示一个颜色选择器对话框,并将选定的颜色存储在一个全局变量中,该变量会被 Apply 按钮的回调过程访问。颜色以 RGB 值列表的形式存储。我们取每个元素,并用以下代码将其转换为 OLE_COLOR
等效项。
# rgbcolor is the list holding the RGB components of the global variable ::g_Color1
set rgbcolor [winfo rgb . $::g_Color1]
set hx 0xff
set r [lindex $rgbcolor 0]
set g [lindex $rgbcolor 1]
set b [lindex $rgbcolor 2]
# code for converting RGB values to UInteger (OLE_COLOR)
set color1 [expr ((($hx & $b) << 16) | (($hx & $g) << 8) | ($hx & $r))]
要访问 Interface
方法,请使用接口变量后跟 -method
选项,然后是方法名称和参数;要设置或获取属性值,请使用 -set
或 -get
选项,后跟属性名称和参数。例如:
$aPoint1 -set X 100
$aPoint2 -set Y 200
$aPoint1 -method Distance $aPoint2
希望您喜欢这篇文章。本文演示的 Tcl 脚本文件和 COM VS2008 项目文件包含在可下载的 zip 文件中。Windows 7 用户需要以管理员模式运行 Visual Studio 来构建项目并注册 COM DLL。
关注点
COM 的强大功能和 IDispatch
再次得到证明。它为从 RAD 工具和脚本访问组件提供了巨大的灵活性。
进一步参考
- http://www.tcl.tk/man/tcl/tutorial/tcltutorial.html
- http://en.wikipedia.org/wiki/Tcl
- http://wiki.tcl.tk/1304
- http://www.tkdocs.com/tutorial/
历史
- 2010 年 4 月 16 日:首次发布