科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件在VB程序中处理随机事件

在VB程序中处理随机事件

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在程序设计过程中,如何轻松地处理众多的随机事件,往往是制作大型系统首先要考虑的问题之一。

作者:蛋蛋  来源:soft6 2008年5月23日

关键字: 事件 处理 VB vb.net Windows

  • 评论
  • 分享微博
  • 分享邮件
在程序设计过程中,如何轻松地处理众多的随机事件,往往是制作大型系统首先要考虑的问题之一。用C语言开发Windows程序时,可以方便地使用消息机制(Message),但是,设计VB程序时,就没有这样的方便条件了。例如,多个窗口同时打开同一个表(Table),当在一个窗口中对数据进行了修改,而其他的窗口也能够随之进行数据更新,这时就需要有一条说明数据改变了的消息在所有的窗口间进行广播。如果使用的语言是C,只需要定义一条用户消息(UserMessage),就可以实现这一点。

  可是如果是用VB编程,做起来就不是那么简单了,最初我是试着这样实现的:
  自定义了一个消息结构(VbMsg),并在程序的主窗体内,建立一个消息广播引擎,主要由一个消息队列和一个定时消息广播器所组成。消息广播器固定隔一定时间检查一次消息队列,如果有消息存在,就将其发送给所有的打开的窗口,并将该消息从队列中删除。如此再定义一个全局的消息发送过程(SendMsg),将要发送的消息(VbMsg)送入消息队列。

  这样当需要广播消息时,只需填充好消息结构,调用SendMsg过程即可。这里较为复杂的是消息广播器如何将消息发送到各窗口:这需要作个硬性规定,就是每一个窗体都必须定义一个形式完全相同的消息接收函数(RecMsg),在这个函数中对接收到的消息进行处理,当然也可以什么都不做。

  有了这样的规定之后,消息广播器在进行广播时,就可以是利用VB系统定义的全局变量Forms,遍历所有的窗体,并调用一遍每个窗体的消息接收函数,其样子大致如下:

  Public Sub SendMsgToForms(msg as VbMsg)

  Dim frm as Form

  For Each frm In Forms

  frm.RecMsg msg

  Next frm

  End Sub


  通过上面的这些过程,就可以实现在独立的程序中,对随机事件进行异步处理。这一方法我曾经在早期开发的几个系统中使用,效果基本还是令人满意的。但是它有几个较大的局限性,当开发更大一些的系统时,就显得不能够满足需要。主要有以下几点:

  1.定时检查消息队列,需要利用Timer控件进行触发。这在程序运行时,就必然要牺牲一部分效率;

  2.消息广播的范围限定在一个程序模块内,如果整个系统分成多个大的模块,那么存在于动态连接模块(.DLL)中的窗体,将不能直接接收到广播消息。而要想实现进程间的消息传递,这一方法就更加不可能;

  3.消息的接收者只能是窗体,而做为真正的基础单元--“类”却无法直接接收消息。

  为了打破上面的几点局限性,就必须寻找新的解决办法。非常庆幸的是,VB5.0企业版的推出,给VB增添了许多强有力的特性,有几点特性,正好可以帮助我们解决难题。先让我介绍一下这几个特性:

  1.用户自定义事件:在类模块中,可以使用Event关键字来定义用户自定义事件,使用RaiseEvent语句来产生该事件,这一机制给处理随机事件带来极大的方便。上面说的消息广播引擎,就可以不再使用Timer控件做支持,而是当收到需要广播的消息时,产生一个预定义的事件,而需要处理消息的客体对象,只需截获该事件,就完成了消息的传递。

  2.ActiveX EXE部件:利用VB,可以方便地将共享代码封装在ActiveX部件之中。将消息广播引擎实现于一个ActiveX部件之中,不仅方便了在程序中使用,而且更为重要的一点是,可以实现跨进程间的消息传递。因为ActiveX部件有内部(DLL)外部(EXE)两种,对于外部部件,可以对模块内的全局数据实现共享(关于ActiveX两种代码部件的区别,请阅读VB的联机帮助文件)。

  3.远程自动化连接:ActiveX部件,是一种标准的客户机/服务器结构,利用Windows平台的COM模型,VB已能方便地将这种结构扩展到整个网络的范围。所以,我们的消息广播设计,在实现了进程间的消息传递之后,进而实现网络上的消息传递,也成为可能。

  通过上面的几点介绍,这一方式的设计思想也就比较清楚了,在具体设计时,我通过四个模块之间的相互协作,完成了消息的发送、广播及接收,并将这四个模块封装在一个ActiveXEXE部件之中。下面就是这三个类模块的简单介绍及源代码:

  类模块之一:Msg.cls----在该模块中,定义了消息数据结构VbMsg类,它是消息传递中的载体。这里只是一个简单的例子,如果想实现更多的功能,如建立两点间的数据通道,而不是单纯的广播消息,则可能需要对该结构进行一些扩充。

  VERSION 1.0 CLASS

  BEGIN

  MultiUse = -1 ''True

  END

  Attribute VB_Name = "VbMsg"

  Attribute VB_GlobalNameSpace = False

  Attribute VB_Creatable = True

  Attribute VB_PredeclaredId = False

  Attribute VB_Exposed = True

  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 属 性 进 行 过 滤。

  VERSION 1.0 CLASS

  BEGIN

  MultiUse = -1 ''True

  END

  Attribute VB_Name = "MsgClient"

  Attribute VB_GlobalNameSpace = False

  Attribute VB_Creatable = True

  Attribute VB_PredeclaredId = False

  Attribute VB_Exposed = True

  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 标 志。 把 变 量 放 在 单 独 的 模 块 中, 是 为 了 实 现 数 据 在 进 程 间 的 共 享, 是 跨 进 程 间 消 息 传 递 的 关 键 所 在。( 应 保 证 在 编 译 时 工 程 是 单 限 程 的, 否 则 数 据 共 享 则 不 能 实 现。)。

  Attribute VB_Name = "modGlobal"

  Option Explicit

  '' 说 明:

  '' 消 息 服 务 器 全 局 变 量

  '' 消 息 接 收 客 户 列 表

  Public Clients As New Collection

  '' 消 息 接 收 客 户ID 计 数 器

  Public CliCount As Long


 

[下一页]


 

  类模块之四:MsgSrv.cls----本模块中定义了消息服务器类MsgServer,该类是消息广播引擎的主体,它主要管理维护消息接收器列表(Clients),将发送来的消息(调用SendMsg过程)依次发送给列表中的所有接收器。注意,这个类被声明为公共全局类,这主要是为了方便使用(不必在每个程序中再建立该类,过程名全局有效)。

  VERSION 1.0 CLASS

  BEGIN

  MultiUse = -1 ''True

  END

  Attribute VB_Name = "MsgServer"

  Attribute VB_GlobalNameSpace = True

  Attribute VB_Creatable = True

  Attribute VB_PredeclaredId = False

  Attribute VB_Exposed = True

  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 

  End Sub


  在程序的重点需要了解的环节插入MsgInfo过程,运行时信息就会在事件查看程序的窗 口中被显示出来。这种方法尤其适合调试多程序协作的软件系统。当软件系统正式交给用 户时,插入的MsgInfo过程也不一定要全部删掉,只要将实时查看变为写入日志文件,这 些运行时的信息也是日后软件维护的第一手资料。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章