摘 要 | 本 文 介 绍 了 VB 4 . 0 插 件 的 基 本 概 念 、 使 用 和 设 计 的 方 法 与 技 巧 , 并 通 过 介 绍 一 个 用 于 快 速 建 立 对 话 框 的 插 件 — — 对 话 框 向 导 Dialog Wizard的 设 计 过 程 , 展 示 了 使 用 VB 4 0 的 类 模 块 编 写 插 件 的 方 法 。 |
---|---|
关 键 词 | Add in, Add on, 类 模 块 , OLE Automation, 内 进 程 OLE服 务 器 , 外 进 程 OLE服 务 器 , 集 成 环 境 |
Visual Basic 4 . 0 允 许 用 户 编 写 插 件 ( Add in) 来 加 强 其 集 成 环 境 ( IDE) 的 功 能 。 插 件 , 就 是 嵌 入 到 VB的 集 成 环 境 中 并 扩 展 其 功 能 的 模 块 , 像 用 户 在 使 用 VB的 以 前 版 本 时 所 熟 悉 的 VBAssist和 VBZ等 , 就 属 于 插 件 。 在 以 前 的 版 本 中 并 没 有 一 个 插 件 的 接 口 , 所 以 编 写 插 件 是 件 相 当 琐 碎 的 事 。 正 因 为 如 此 , 以 前 极 少 有 人 为 VB编 写 插 件 。 现 在 , 插 件 可 以 通 过 一 组 公 开 的 接 口 来 同 VB 4 0 交 换 数 据 了 , 这 组 接 口 是 由 自 动 OLE( OLE Automation) 来 实 现 。 用 自 动 OLE来 定 义 插 件 API并 不 是 VB 4 . 0 的 创 举 , 事 实 上 , 自 动 OLE的 一 个 主 要 目 的 , 就 是 为 了 提 供 软 件 间 通 信 的 工 具 。 同 一 些 采 用 专 用 的 宏 语 言 编 写 插 件 的 软 件 系 统 相比 较, 利 用 自 动 OLE 的 优 点 是 明 显 的 : 一 方 面 , 支 持 自 动 OLE的 开 发 工 具 很 多 , 用 户 可 以 从 中 选 择 自 己 所 熟 悉 的 ; 另 一 方 面 , 自 动 OLE是 用 属 性 和 方 法 这 样 的 概 念 来 描 述 的 , 更 符 合 面 向 对 象 设 计 的 潮 流 。
VB 4 . 0 的 插 件 是 一 种 特 殊 的 自 动 OLE应 用 。 在 1 6 位 版 本 的 VB 4 . 0 中 , 插 件 必 须 是 EXE程 序 , 而 在 3 2 位 版 本 中 , 可 以 为 EXE或 DLL程 序 。 按 照 自 动 OLE术 语 , DLL应 用 被 称 为 内 进 程 OLE服 务 器 , 而 EXE应 用 称 为 外 进 程 OLE服 务 器 。 由 于 DLL程 序 是 运 行 在 客 户 程 序 ( 在 这 里 是 VB 4 . 0 ) 的 进 程 空 间 内 , 调 用 速 度 较 EXE应 用 快 , 但 编 程 时 受 到 一 些 限 制 。 绝 大 多 数 自 动 OLE的 EXE程 序 还 支 持 命 令 行 注 册 方 式 , 即 可 以 在 命 令 行 加 入 / REGSERVER参 数 , 程 序 自 动 完 成 在 系 统 数 据 库 中 的 注 册 工 作 。
在 使 用 插 件 之 前 , 先 要 确 保 该 插 件 已 在 系 统 数 据 库 中 进 行 了 正 确 的 注 册 , 然 后 在 VB. INI中 进 行 登 记 : 1 6 位 版 本 应 在 [ Add Ins1 6 ] 节 中 加 入 注 册 行 ; 而 3 2 位 版 本 加 入 在 [ Add Ins3 2 ] 节 中 , 两 个 版 本 的 插 件 不 能 混 用 。 每 个 注 册 行 的 格 式 为 :
服 务 器 名 . 类 名 = 1 或 0 。
如 : DialogWizard. DlgClass= 0 。
在 这 个 例 子 中 , VB 4 . 0 会 自 动 在 系 统 数 据 库 中 搜 索 并 调 用 DialogWizard. DlgClass。 等 号 后 的 数 值 1 表 示 , VB 4 . 0 在 每 次 启 动 之 后 均 会 调 用 该 插 件 ; 0 则 表 示 必 须 通 过 Add In Manager调 用 插 件 , 这 时 , 可 以 选 择 VB 4 . 0 的 Tools| Add In Manager菜 单 项 , 出 现 图 1 所 示 的 对 话 框 , 在 对 话 框 中 选 择 所 需 的 插 件 。 登 记 过 程 可 以 手 工 完 成 , 但 多 数 插 件 是 由 安 装 程 序 或 插 件 本 身 来 完 成 的 。 多 数 插 件 会 在 调 用 后 在 Add ins菜 单 下 建 立 自 己 的 菜 单 项 , 以 实 现 其 功 能 。
VB 4 . 0 提 供 了 三 个 插 件 范 例 程 序 ALIGN、 DFD、 SPY, 不 仅 展 示 了 插 件 设 计 的 基 本 技 巧 , 同 时 也 是 非 常 实 用 的 工 具 。
VB 4 . 0 的 插 件 API, 包 括 事 件 、 属 性 、 方 法 、 对 象 和 集 合 ( Collection) 。 插 件 响 应 VB 4 . 0 的 事 件 请 求 , 并 通 过 VB 4 . 0 提 供 的 属 性 、 方 法 、 对 象 和 集 合 来 控 制 VB 4 . 0 的 行 为 。
VB 4 . 0 提 供 的 对 象 和 集 合 分 为 几 个 层 次 ( 图 2 ) 。 最 高 层 为 Application对 象 , 它 表 明 调 用 插 件 的 VB 4 . 0 实 例 。 在 同 一 时 刻 , 插 件 必 须 先 获 得 这 个 对 象 , 然 后 才 能 访 问 插 件 API。 插 件 通 过 SubMenu对 象 在 VB 4 . 0 的 IDE的 Add ins菜 单 中 增 加 自 己 的 菜 单 项 。 一 般 的 插 件 总 是 用 来 完 成 两 类 工 作 , 一 类 是 控 制 VB IDE的 文 件 I/ O, 另 一 类 是 操 纵 正 在 编 辑 的 项 目 、 窗 体 和 控 制 。 插 件 通 过 建 立 同 FileControl对 象 的 连 接 可 以 响 应 各 种 与 文 件 操 作 有 关 的 事 件 。 例 如 , 每 当 用 户 要 调 一 个 文 件 到 IDE中 时 , 插 件 可 以 告 诉 VB 4 . 0 应 先 做 什 么 工 作 。 Application对 象 有 一 个 ActiveProject属 性 , 它 是 个 ProjectTemplate对 象 , 表 示 当 前 在 IDE中 编 辑 的 工 程 文 件 ( . VBP) , 通 过 该 对 象 可 以 访 问 工 程 文 件 中 的 所 有 窗 体 文 件 、 代 码 文 件 ( . BAS) 、 类 模 块 文 件 ( . CLS) 和 资 源 文 件 ( . RES) 。 通 过 FormTemplate对 象 可 以 访 问 窗 体 的 属 性 及 其 内 各 控 制 的 属 性 。
插 件 在 运 行 时 可 以 响 应 VB 4 . 0 的 事 件 请 求 。 VB 4 . 0 的 消 息 和 相 对 应 的 插 件 操 作 见 表 1 。 VB 4 . 0 并 未 对 事 件 的 处 理 做 太 多 的 限 制 , 表 中 的 插 件 操 作 只 是 笔 者 根 据 实 际 经 验 所 提 出 的 建 议 。
事件 | 插件操作 |
AfterAddFile | 按照习惯或约定改变文件属性,插件内部数据初始化 |
AfterChangeFileName | 删除原文件,改变插件内部数据 |
AfterClick | 显示插件对话框 |
AfterCloseFile | 改变插件内部数据 |
AfterNewProject | 插件内部数据初始化 |
AfterRemoveFile | 删除原文件,改变插件内部数据 |
AfterWriteFile | 备份文件,在文件内保存私有信息,转换文件格式 |
BeforeLoadFile | 转换文件格式,源程序处理 |
CheckIn | 源程序处理(响应Tools|Check In菜单项) |
CheckOut | 源程序处理(响应Tools|Check Out菜单项) |
ConnectAddIn | 获取Application对象,添加VBIDE菜单项 |
DisconnectAddIn | 卸载插件 |
DoGetAddFileName | 提供自定义的对话框 |
DoGetNewFileName | 提供自定义的对话框 |
DoGetOpenProjectName | 提供自定义的对话框 |
Fetch | 源程序处理(响应Tools|Get菜单项) |
RequestChangeFileName | 接受或拒绝文件改名 |
RequestWriteFile | 是否写文件,源程序处理 |
UnCheckOut | 源程序处理 |
VB 4 . 0 的 企 业 版 ( Enterprise Edition) 中 , 提 供 了 Visual SourceSafe 4 . 0 工 具 来 维 护 软 件 的 源 程 序 , 用 户 可 以 通 过 Tools菜 单 下 的 一 些 项 进 行 调 用 , 它 也 是 一 个 VB 4 . 0 的 插 件 , 表 中 的 几 个 “ 源 程 序 处 理 ” 事 件 就 是 专 门 为 其 设 计 , 如 果 用 户 设 计 的 插 件 有 类 似 功 能 , 也 应 处 理 这 些 事 件 。
VB 4 . 0 的 插 件 , 可 以 使 用 任 何 一 种 支 持 自 动 OLE服 务 器 的 软 件 进 行 设 计 , 如 Visual C+ + 、 Borland C+ + 、 Delphi, 当 然 这 其 中 也 包 括 VB 4 . 0 。 任 何 一 个 插 件 必 须 包 括 一 个 自 动 OLE类 , 所 以 在 VB的 工 程 文 件 中 至 少 应 包 括 一 个 类 模 块 , 这 个 类 模 块 的 Public属 性 必 须 是 True, 而 且 由 于 VB 4 . 0 允 许 同 时 运 行 多 个 实 例 , 该 模 块 的 Instancing属 性 应 为 2 , 即 允 许 多 个 实 例 创 建 对 象 。 这 个 类 模 块 中 应 包 括 ConnectAddIn 和 DisconnectAddIn事 件 。 ConnectAddIn 事 件 用 来 获 得 Application对 象 , 如 果 插 件 需 要 在 VB 4 . 0 的 集 成 环 境 中 增 加 菜 单 , 可 以 在 此 时 用 Application. AddInMenu属 性 ( SubMenu对 象 类 型 ) 加 入 菜 单 项 , 并 用 ConnectEvents方 法 建 立 同 菜 单 项 的 连 接 ; 如 果 插 件 需 要 响 应 文 件 操 作 事 件 , 应 先 获 得 Application. FileControl属 性 。 VBIDE. MenuItems和 VBIDE. MenuLine对 象 和 由 此 获 得 的 ID值 都 不 要 存 放 在 全 局 变 量 中 , 而 应 保 存 在 类 模 块 中 , 这 是
因 为 一 个 插 件 可 能 同 时 运 行 多 个 实 例 , 每 个 实 例 都 独 立 维 护 各 自 类 模 块 内 的 数 据 而 共 用 全 局 变 量 , 将 数 据 保 存 在 全 局 变 量 中 可 能 引 起 混 乱 。 DisconnectAddIn事 件 用 来 卸 载 插 件 。 下 面 便 是 一 个 典 型 的 插 件 类 模 块 , 它 来 完 成 与 IDE的 连 接 和 对 IDE事 件 的 响 应 。
Dim AddInItems As Object ′VBIDE.MenuItems
Dim DlgMenu As Object ′VBIDE.MenuLine
Dim ConnectID As Long
′ 添加插件
Sub ConnectAddIn(VBInst As VBIDE.Application)
On Error GoTo error-handler
Set gobjIDEAppInst=VBInst ′全局变量
Set AddInItems=VBInst.AddInMenu.MenuItems
Set DlgMenu=AddInItems.Add(″对话框向导...″)
ConnectID=DlgMenu.ConnectEvents(Me)
MsgBox ″对话框向导1.0版″+Chr$(13)+Chr$(13)+-
″版权所有 1996 热情软件屋″, vbInformation
Exit Sub
error-handler:
MsgBox Err.Description
Exit Sub
End Sub
′ 去除插件
Sub DisconnectAddIn(ByVal mode As Integer)
′ 去除插件的事件句柄
DlgMenu.DisconnectEvents ConnectID
′ 删除VBIDE的菜单项
AddInItems.Remove DlgMenu
End Sub
ConnectAddIn事件中,使用 DlgMenu.ConnectEvents(Me)建立同
VBIDE菜单的联系,一旦用户选择菜单项,VB 4.0就调用该类模块的
AfterClick事件。下面的代码利用该事件来显示窗体frmDlgWiz。
′ 激活向导
Sub AfterClick()
frmDlgWiz.Show vbModal
End Sub
我们建议插件利用Main过程自动修改VB.INI,加入注册信息,这样用户
使用插件就更简单了。
′程序名称:对话框向导
′目 的:快速建立对话框的VB 4.0插件
Global gobjIDEAppInst As Object
#If Win16 Then
Declare Function OSWritePrivateProfileString% Lib ″KERNEL″ Alias ″WritePrivateProfileString″ (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal FileName$)
Declare Function OSGetPrivateProfileString% Lib ″KERNEL″ Alias ″GetPrivateProfileString″ (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal ReturnString$, ByVal NumBytes As Integer, ByVal FileName$)
#Else
Declare Function OSWritePrivateProfileString% Lib ″Kernel32″ Alias ″WritePrivateProfileStringA″ (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal FileName$)
Declare Function OSGetPrivateProfileString% Lib ″Kernel32″ Alias ″GetPrivateProfileStringA″ (ByVal AppName$, ByVal KeyName$, ByVal keydefault$, ByVal ReturnString$, ByVal NumBytes As Integer, ByVal FileName$)
#End If
′ 程序人口
′ 如果VB.INI中未包含注册消息,进行注册
Sub Main()
Dim ReturnString As String
′ 检查VB.INI中是否包含注册消息
#If Win16 Then
Section$=″AddIns16″
#Else
Section$=″AddIns32″
#End If
ReturnString=String$(12, Chr$(0))
ErrCode=OSGetPrivateProfileString(Section$, ″DialogWizard.DlgClass″, ″NotFound″, ReturnString, Len(ReturnString) + 1, ″VB.INI″)
If Left(ReturnString, ErrCode)=″NotFound″ Then
ErrCode=OSWritePrivateProfileString%(Section$, ″DialogWizard.DlgClass″, ″0″, ″VB.INI″)
End If
End Sub
下 面 我 们 将 利 用 前 面 的 类 模 块 代 码 和 Main过 程 来 建 立 一 个 实 用 的 插 件 — — 对 话 框 向 导 Dialog Wizard, 这 个 插 件 的 目 标 是 快 速 建 立 对 话 框 , 一 旦 用 户 选 择 “ Add Ins| 对 话 框 向 导 . . . ” 菜 单 项 , 就 显 示 对 话 框 , 提 示 用 户 输 入 对 话 框 的 名 字 ( 即 Name属 性 ) 和 标 题 , 然 后 插 件 在 当 前 工 程 文 件 中 加 入 一 个 窗 体 文 件 , 设 置 相 应 的 属 性 并 插 入 所 需 的 代 码 。
首 先 , 建 立 图 3 所 示 的 窗 体 。 并 将 该 窗 体 的 Name属 性 设 为 frmDlgWiz。 然 后 , 在 窗 体 代 码 窗 中 加 入 如 下 程 序 。
Option Explicit
#If Win16 Then
DefInt A - Z
Private Declare Sub RemoveMenu Lib ″User″ (ByVal hMenu, ByVal nPos, ByVal wFlags)
Private Declare Function GetSystemMenu Lib ″User″ (ByVal hwnd, ByVal bRevert)
Const MF-BYPOSITION=&H400
#End If
Private Sub Command1-Click() ′“确定”按钮
Dim NewDlg As FormTemplate
On Error GoTo ErrHandle
Set NewDlg=gobjIDEAppInst.ActiveProject.AddFormTemplate
NewDlg.Properties.Item(″Name″)=Text1.Text
NewDlg.Properties.Item(″Caption″)=Text2.Text
NewDlg.Properties.Item(″BorderStyle″)=3
NewDlg.InsertFile App.Path + ″\DLGPROC.BAS″
Unload Me
Exit Sub
ErrHandle:
MsgBox Err.Description, vbCritical
Unload Me
End Sub
Private Sub Command2-Click() ′“取消”按钮
Unload Me
End Sub
#If Win16 Then
Private Sub Form-Load()
Dim HSysMenu
HSysMenu=GetSystemMenu(Me.hwnd, 0)
′删除系统菜单上除了“移动”和“关闭”以外的所有项
RemoveMenu HSysMenu, 8, MF-BYPOSITION ′“切换到”
RemoveMenu HSysMenu, 7, MF-BYPOSITION ′分割符
RemoveMenu HSysMenu, 5, MF-BYPOSITION ′分割符
End Sub
#End If
Private Sub Text1-Change()
Command1.Enabled=Text1.Text <> ″″
End Sub
在 Command1 - Click过 程 中 , 我 们 先 利 用 Set NewDlg= gobjIDEAppInst. ActiveProject. AddFormTemplate语 句 在 当 前 工 程 文 件 中 加 入 一 个 新 窗 体 , 然 后 利 用 NewDlg. Properties集 合 的 Items属 性 设 置 该 窗 体 的 属 性 。 VB 4 . 0 中 , 对 话 框 与 普 通 窗 体 的 最 主 要 的 区 别 是 对 话 框 的 BorderStyle属 性 为 3 。 对 于 VB 4 . 0 的 3 2 位 版 本 来 说 , 设 置 这 一 属 性 就 可 以 了 , 但 对 于 1 6 位 版 本 来 说 , 这 样 的 对 话 框 的 系 统 菜 单 上 还 会 残 留 下 来 “ 切 换 到 … ” 菜 单 项 , 看 起 来 有 些 别 扭 , Form- Load过 程 就 是 用 来 去 除 这 部 分 系 统 菜 单 的 , 这 对 每 个 对 话 框 来 说 是 必 须 的 , 我 们 将 这 部 分 代 码 事 先 保 存 在 DLGPROC. BAS文 件 中 , 然 后 用 NewDlg. InsertFile方 法 将 其 插 入 对 话 框 的 代 码 窗 中 。 顺 便 说 一 句 , 在 VB3 中 如 果 没 有 这 段 代 码 , 就 无 法 真 正 地 实 现 排 他 式 对 话 框 ( modal dialog) 。
至 此 , 一 个 “ 对 话 框 向 导 ” 插 件 就 设 计 好 了 , 编 译 成 EXE或 DLL文 件 就 可 以 运 行 了 。
如果您有任何建议,请给我发电子邮件:
。
版权所有 李海,热情软件屋 1997-2006