科技行者

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

知识库

知识库 安全导航

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

VB程序中处理随机事件

  • 扫一扫
    分享文章到微信

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

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

来源:soft6 2008年5月13日

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

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

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

---- 通 过 上 面 的 这 些 过 程, 就 可 以 实 现 在 独 立 的 程 序 中, 对 随 机 事 件 进 行 异 步 处 理。 这 一 方 法 我 曾 经 在 早 期 开 发 的 几 个 系 统 中 使 用, 效 果 基 本 还 是 令 人 满 意 的。 但 是 它 有 几 个 较 大 的 局 限 性, 当 开 发 更 大 一 些 的 系 统 时, 就 显 得 不 能 够 满 足 需 要。 主 要 有 以 下 几 点:
定 时 检 查 消 息 队 列, 需 要 利 用Timer 控 件 进 行 触 发。 这 在 程 序 运 行 时, 就 必 然 要 牺 牲 一 部 分 效 率;

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

消 息 的 接 收 者 只 能 是 窗 体, 而 做 为 真 正 的 基 础 单 元--“ 类” 却 无 法 直 接 接 收 消 息。
---- 为 了 打 破 上 面 的 几 点 局 限 性, 就 必 须 寻 找 新 的 解 决 办 法。 非 常 庆 幸 的 是, V B 5.0 企 业 版 的 推 出, 给VB 增 添 了 许 多 强 有 力 的 特 性, 有 几 点 特 性, 正 好 可 以 帮 助 我 们 解 决 难 题。 先 让 我 介 绍 一 下 这 几 个 特 性:
用 户 自 定 义 事 件: 在 类 模 块 中, 可 以 使 用Event 关 键 字 来 定 义 用 户 自 定 义 事 件, 使 用 RaiseEvent 语 句 来 产 生 该 事 件, 这 一 机 制 给 处 理 随 机 事 件 带 来 极 大 的 方 便。 上 面 说 的 消 息 广 播 引 擎, 就 可 以 不 再 使 用Timer 控 件 做 支 持, 而 是 当 收 到 需 要 广 播 的 消 息 时, 产 生 一 个 预 定 义 的 事 件, 而 需 要 处 理 消 息 的 客 体 对 象, 只 需 截 获 该 事 件, 就 完 成 了 消 息 的 传 递。

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

远 程 自 动 化 连 接:ActiveX 部 件, 是 一 种 标 准 的 客 户 机/ 服 务 器 结 构, 利 用Windows 平 台 的COM 模 型,VB 已 能 方 便 地 将 这 种 结 构 扩 展 到 整 个 网 络 的 范 围。 所 以, 我 们 的 消 息 广 播 设 计, 在 实 现 了 进 程 间 的 消 息 传 递 之 后, 进 而 实 现 网 络 上 的 消 息 传 递, 也 成 为 可 能。
---- 通 过 上 面 的 几 点 介 绍, 这 一 方 式 的 设 计 思 想 也 就 比 较 清 楚 了, 在 具 体 设 计 时, 我 通 过 四 个 模 块 之 间 的 相 互 协 作, 完 成 了 消 息 的 发 送、 广 播 及 接 收, 并 将 这 四 个 模 块 封 装 在 一 个ActiveX EXE 部 件 之 中。 下 面 就 是 这 三 个 类 模 块 的 简 单 介 绍 及 源 代 码:
类 模 块 之 一: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 程 序 时, 往 往 会 感 觉 到 程 序 的 实 际 运 行 过 程 与 你 想 象 的 相 差 甚 远, 调 试 时 就 非 常 希 望 看 到 程 序 运 行 时 后 台 的 一 些 情 况。 利 用 V B 的 单 步 执 行 或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领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

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