在程序设计过程中,如何轻松地处理众多的随机事件,往往是制作大型系统首先要考虑的问题之一。用c语言开发windows程序时,可以方便地使用消息机制(message),但是,设计vb程序时,就没有这样的方便条件了。例如,多个窗口同时打开同一个表(table),当在一个窗口中对数据进行了修改,而其他的窗口也能够随之进行数据更新,这时就需要有一条说明数据改变了的消息在所有的窗口间进行广播。如果使用的语言是c,只需要定义一条用户消息(usermessage),就可以实现这一点。可是如果是用vb编程,做起来就不是那么简单了。VB5.0企业版中增添了许多强有力的特性,有几点特性,正好可以帮助我们解决难题。先介绍一下这几个特性:
1.用户自定义事件:在类模块中,可以使用event关键字来定义用户自定义事件,使用raiseevent语句来产生该事件,这一机制给处理随机事件带来极大的方便。当收到需要广播的消息时,产生一个预定义的事件,而需要处理消息的客体对象,只需截获该事件,就完成了消息的传递。
2.activex exe部件:利用vb,可以方便地将共享代码封装在activex部件之中。将消息广播引擎实现于一个activex部件之中,不仅方便了在程序中使用,而且更为重要的一点是,可以实现跨进程间的消息传递。因为activex部件有内部(dll)外部(exe)两种,对于外部部件,可以对模块内的全局数据实现共享(关于activex两种代码部件的
区别,请阅读vb的联机帮助文件)。
3.远程自动化连接:activex部件,是一种标准的客户机/服务器结构,利用windows平台的com模型,vb已能方便地将这种结构扩展到整个网络的范围。所以,我们的消息广播设计,在实现了进程间的消息传递之后,进而实现网络上的消息传递,也成为可能。
通过上面的几点介绍,这一方式的设计思想也就比较清楚了,在具体设计时,通过四个模块之间的相互协作,完成了消息的发送、广播及接收,并将这四个模块封装在一个activexexe部件之中。下面就是这三个类模块的简单介绍及源代码:
类模块之一:msg.cls----在该模块中,定义了消息数据结构vbmsg类,它是消息传递中的载体。这里只是一个简单的例子,如果想实现更多的功能,如建立两点间的数据通道,而不是单纯的广播消息,则可能需要对该结构进行一些扩充。
option explicit
/ 说 明:
/ 消 息 类: 定 义 全 局 的 消 息 结 构
public itype as long /消 息 类 型 编 号
public iname as string /消 息 名
public isource as string / 消 息 源 说 明
public idescription as string /消 息 说 明
dim it as date / 消 息 发 生 时 间
/返 回 日 期 型 时 间
public property get itime() as date
itime = it
end property
/返 回 字 符 型 时 间
public property get itimestr() as string
itimestr = format(it, "yyyy.mm.dd hh:mm:ss")
end property
/ 在 对 象 被 建 立 时, 设 置 消 息 发 生 时 间
private sub class_initialize()
it = now()
end sub
类模块之二:msgcli.cls ---- 本模块是对客户接收端msgclient类的定义,这相当于一
个消息接收器。在这个类中定义的一个recmsg事件,当接收器收到消息时(过程setmsg被
调用),就产生这一事件.接收器的建立者就截获这一事件,并处理消息。为了避免接收不
必要的消息,声明了minmsg、maxmsg两个变量,以便对vbmsg中的itype属性进行过
滤。
option explicit
' 说 明:
' 客 户 消 息 接 收 类
' 定 义 接 收 消 息 事 件, 该 对 象 的 宿 主 类 应 截 获 该 事 件,
并 处 理接 收 到 的 消 息。
public event recmsg(byval msg as vbmsg)
' 通 过 设 置 消 息 的 接 收 范 围, 可 以 过 滤 掉 不 需 要 的 消息
public minmsg as long
public maxmsg as long
' 该 对 象 的 标 志 编 号, 使 用 时 不 应 修 改 该 值
public id as long
' 事 件 产 生 过 程, 只 应 由 消 息 服 务 器(msgserver) 调 用
public sub setmsg(msg as vbmsg)
if msg.itype >= minmsg and msg.itype <= maxmsg then raiseevent recmsg(msg)
end if end sub ' '
根 据id, 返 回 对 象 的 关 键 字, 只 应 由 消 息 服 务 器(msgserver)调 用
public property get key() as string key="id:" & id end property
类模块之三:global.bas ---- 本模块声明了两个全局变量,一个是接收器(msgclient)列表(clients),一个是接收器计数器,以为每个接收器分配一个唯一的id标志。把变量放在单独的模块中,是为了实现数据在进程间的共享,是跨进程间消息传递的关键所在。(应保证在编译时工程是单线程的,否则数据共享则不能实现。)。
option explicit
/ 说 明:
/ 消 息 服 务 器 全 局 变 量
/消 息 接 收 客 户 列 表
public clients as new collection
/ 消 息 接 收 客 户id 计 数 器
public clicount as long
类模块之四:msgsrv.cls----本模块中定义了消息服务器类msgserver,该类是消息广播引擎的主体,它主要管理维护消息接收器列表(clients),将发送来的消息(调用sendmsg过程)依次发送给列表中的所有接收器。注意,这个类被声明为公共全局类,这主要是为了方便使用(不必在每个程序中再建立该类,过程名全局有效)。
option explicit
/说 明:
/消 息 服 务 器 类
/发 送 消 息
public sub sendmsg(msg as vbmsg)
dim c as msgclient
for each c in clients
c.setmsg msg
doevents
next c
end sub
/增 删 消 息 接 收 客 户
public sub addmsgclient(c as msgclient)
clicount = clicount + 1
c.id = clicount
clients.add c, c.key
end sub
public sub delmsgclient(c as msgclient)
clients.remove c.key
if clients.count = 0 then clicount = 0
end sub
到这里,一个小巧灵活的消息广播引擎就完成了,它的使用范围很广,用起来也很方便,只需在工程中引入编译过的activex部件,就可以直接调用sendmsg发送消息,可能在安装消息接收器(msgclient)时会稍许有点麻烦,下面举一个简单的应用例子大致说明一下:
在设计windows程序时,往往会感觉到程序的实际运行过程与你想象的相差甚远,调试时就非常希望看到程序运行时后台的一些情况。利用vb的单步执行或debug命令,都会受到一些限制。利用消息广播引擎,制作一个通用的实时消息事件查看程序,就可以很好地解决这一问题。查看程序的主要工作就是捕捉一组事先定义好的消息事件,并将消息的内容显示在列表框内,可以只用一个窗体完成,大体样子如下:
const msginfoid=101
private withevents mclient as msgclient
private sub form_load()
set mclient = new msgclient
mclient.minmsg= msginfoid
mclient.maxmsg= msginfoid
addmsgclient mclient
end sub
private sub form_unload(cancel as integer)
delmsgclient mclient
end sub
private sub mclient _recmsg(byval msg as vbmsgsrv.vbmsg)
list1.additem msg.itimestr & chr(9) & msg.iname & chr(9) & msg.idescription
end sub
在被调试的程序中,为了调用方便,可以编写一个全局过程,象下面这个样子:
const msginfoid=101
public sub msginfo(iname as string,ides as string)
dim msg as new msgclient
with msg
.iname = iname
. idescription = ides
end with
sendmsg msg