ATL 介 绍( 一)
吕 思 伟 ---- 潘 爱 民
一、 什 么 是ATL
---- 1 .COM 的 发 展 及 其 局 限 性
---- 自 从1993 年Microsoft 首 次 公 布 了COM 技 术 以 后,Windows 平 台 上 的 开 发 模 式 发 生 了 巨 大 的 变 化, 以COM 为 基 础 的 一 系 列 软 件 组 件 化 技 术 将Windows 编 程 带 入 了 组 件 化 时 代。 广 大 开 发 人 员 在 为COM 带 来 的 软 件 组 件 化 趋 势 欢 欣 鼓 舞 的 同 时, 对 于COM 开 发 技 术 的 难 度 和 烦 琐 的 细 节 也 感 到 极 其 的 不 便。COM 编 程 一 度 被 视 为 一 种 高 不 可 攀 的 技 术, 令 人 望 而 却 步。 开 发 人 员 希 望 能 够 有 一 种 方 便 快 捷 的COM 开 发 工 具, 提 高 开 发 效 率, 更 好 地 利 用 这 项 技 术。
---- 针 对 这 种 情 况,Microsoft 公 司 在 推 出COM SDK 以 后, 为 简 化COM 编 程, 提 高 开 发 效 率, 采 取 了 许 多 方 案, 特 别 是 在MFC(Microsoft Foundation Class) 中 加 入 了 对COM 和OLE 的 支 持。 但 是 随 着Internet 的 发 展, 分 布 式 的 组 件 技 术 要 求COM 组 件 能 够 在 网 络 上 传 输, 而 又 尽 量 节 约 宝 贵 的 网 络 带 宽 资 源。 采 用MFC 开 发 的COM 组 件 由 于 种 种 限 制 不 能 很 好 地 满 足 这 种 需 求, 因 此Microsoft 在1995 年 又 推 出 了 一 种 全 新 的COM 开 发 工 具 — —ATL。
---- ATL 是ActiveX Template Library 的 缩 写, 它 是 一 套C++ 模 板 库。 使 用ATL 能 够 快 速 地 开 发 出 高 效、 简 洁 的 代 码, 同 时 对COM 组 件 的 开 发 提 供 最 大 限 度 的 代 码 自 动 生 成 以 及 可 视 化 支 持。 为 了 方 便 使 用, 从Microsoft Visual C++ 5.0 版 本 开 始,Microsoft 把ATL 集 成 到Visual C++ 开 发 环 境 中。1998 年9 月 推 出 的Visual Studio 6.0 集 成 了ATL 3.0 版 本。 目 前,ATL 已 经 成 为Microsoft 标 准 开 发 工 具 中 的 一 个 重 要 成 员, 日 益 受 到C++ 开 发 人 员 的 重 视。
---- ATL 究 竟 给 开 发 人 员 带 来 了 什 么 样 的 益 处 呢 ? 这 要 先 从ATL 产 生 以 前 的COM 开 发 方 式 说 起。
---- 在ATL 产 生 以 前, 开 发COM 组 件 的 方 法 主 要 有 两 种: 一 是 使 用COM SDK 直 接 开 发COM 组 件, 另 一 种 方 式 是 通 过MFC 提 供 的COM 支 持 来 实 现。
---- 直 接 使 用COM SDK 开 发COM 组 件 是 最 基 本 也 是 最 灵 活 的 方 式。 通 过 使 用Microsoft 提 供 的 开 发 包, 我 们 可 以 直 接 编 写COM 程 序。 但 是, 这 种 开 发 方 式 的 难 度 和 工 作 量 都 很 大, 一 方 面, 要 求 开 发 者 对 于COM 的 技 术 原 理 具 有 比 较 深 入 的 了 解( 虽 然 对 技 术 本 身 的 深 刻 理 解 对 使 用 任 何 一 种 工 具 都 是 非 常 有 益 的, 但 对 于COM 这 样 一 整 套 复 杂 的 技 术 而 言, 在 短 时 间 内 完 全 掌 握 是 很 难 的); 另 一 方 面, 直 接 使 用COM SDK 要 求 开 发 人 员 自 己 去 实 现COM 应 用 的 每 一 个 细 节, 完 成 大 量 的 重 复 性 工 作。 这 样 做 的 结 果 是, 不 仅 降 低 了 工 作 效 率, 同 时 也 使 开 发 人 员 不 得 不 把 许 多 精 力 投 入 到 与 应 用 需 求 本 身 无 关 的 技 术 细 节 中。 虽 然 这 种 开 发 方 式 对 于 某 些 特 殊 的 应 用 很 有 必 要, 但 这 种 编 程 方 式 并 不 符 合 组 件 化 程 序 设 计 方 法 所 倡 导 的 可 重 用 性, 因 此, 直 接 采 用COM SDK 不 是 一 种 理 想 的 开 发 方 式。
---- 使 用MFC 提 供 的COM 支 持 开 发COM 应 用 可 以 说 在 使 用COM SDK 基 础 上 提 高 了 自 动 化 程 度, 缩 短 了 开 发 时 间。MFC 采 用 面 向 对 象 的 方 式 将COM 的 基 本 功 能 封 装 在 若 干MFC 的C++ 类 中, 开 发 者 通 过 继 承 这 些 类 得 到COM 支 持 功 能。 为 了 使 派 生 类 方 便 地 获 得COM 对 象 的 各 种 特 性,MFC 中 有 许 多 预 定 义 宏, 这 些 宏 的 功 能 主 要 是 实 现COM 接 口 的 定 义 和 对 象 的 注 册 等 通 常 在COM 对 象 中 要 用 到 的 功 能。 开 发 者 可 以 使 用 这 些 宏 来 定 制COM 对 象 的 特 性。
---- 另 外, 在MFC 中 还 提 供 对Automation 和ActiveX Control 的 支 持, 对 于 这 两 个 方 面,Visual C++ 也 提 供 了 相 应 的AppWizard 和ClassWizard 支 持, 这 种 可 视 化 的 工 具 更 加 方 便 了COM 应 用 的 开 发。
---- MFC 对COM 和OLE 的 支 持 确 实 比 手 工 编 写COM 程 序 有 了 很 大 的 进 步。 但 是MFC 对COM 的 支 持 还 不 够 完 善 和 彻 底, 例 如 对COM 接 口 定 义 的IDL 语 言,MFC 并 没 有 任 何 支 持, 此 外 对 于 近 些 年 来COM 和ActiveX 技 术 的 新 发 展MFC 也 没 有 提 供 灵 活 的 支 持。 这 是 由MFC 设 计 的 基 本 出 发 点 决 定 的。MFC 被 设 计 成 对Windows 平 台 编 程 开 发 的 面 向 对 象 的 封 装, 自 然 要 涉 及Windows 编 程 的 方 方 面 面,COM 作 为Windows 平 台 编 程 开 发 的 一 个 部 分 也 得 到MFC 的 支 持, 但 是MFC 对COM 的 支 持 是 以 其 全 局 目 标 为 出 发 点 的, 因 此 对COM 的 支 持 必 然 要 服 从 其 全 局 目 标。 从 这 个 方 面 而 言,MFC 对COM 的 支 持 不 能 很 好 地 满 足 开 发 者 的 要 求。
---- 随 着Internet 技 术 的 发 展,Microsoft 将ActiveX 技 术 作 为 其 网 络 战 略 的 一 个 重 要 组 成 部 分 大 力 推 广, 然 而 使 用MFC 开 发 的ActiveX Control, 代 码 冗 余 量 大, 即 所 谓 的“ 肥 代 码”(Fat Code), 而 且 必 须 要 依 赖 于MFC 的 运 行 时 刻 库 才 能 正 确 地 运 行。 虽 然MFC 的 运 行 时 刻 库 只 有 部 分 功 能 与COM 有 关, 但 是 由 于MFC 的 继 承 实 现 的 本 质,ActiveX Control 必 须 背 负 运 行 时 刻 库 这 个 沉 重 的 包 袱。 如 果 采 用 静 态 连 接MFC 运 行 时 刻 库 的 方 式, 这 将 使ActiveX Control 代 码 过 于 庞 大, 在 网 络 上 传 输 时 将 占 据 宝 贵 的 网 络 带 宽 资 源; 如 果 采 用 动 态 连 接MFC 运 行 时 刻 库 的 方 式, 这 将 要 求 浏 览 器 一 方 必 须 具 备MFC 的 运 行 时 刻 库 支 持。 总 之,MFC 对COM 技 术 的 支 持 在 网 络 应 用 的 环 境 下 也 显 得 很 不 灵 活。
---- 2 .ATL 的 目 标
---- 解 决 上 述COM 开 发 方 法 中 的 问 题 正 是ATL 的 基 本 目 标。
---- 首 先,ATL 的 基 本 目 标 就 是 使COM 应 用 开 发 尽 可 能 地 自 动 化, 这 个 基 本 目 标 就 决 定 了ATL 只 面 向COM 开 发 提 供 支 持。 目 标 的 明 确 使ATL 对COM 技 术 的 支 持 达 到 淋 漓 尽 致 的 地 步。 对COM 开 发 的 任 何 一 个 环 节 和 过 程,ATL 都 提 供 支 持, 并 将 与COM 开 发 相 关 的 众 多 工 具 集 成 到 一 个 统 一 的 编 程 环 境 中。 对 于COM/ActiveX 的 各 种 应 用,ATL 也 都 提 供 了 完 善 的Wizard 支 持。 所 有 这 些 都 极 大 地 方 便 了 开 发 者 的 使 用, 使 开 发 者 能 够 把 注 意 力 集 中 在 与 应 用 本 身 相 关 的 逻 辑 上。
---- 其 次,ATL 因 其 采 用 了 特 定 的 基 本 实 现 技 术, 摆 脱 了 大 量 冗 余 代 码, 使 用ATL 开 发 出 来 的COM 应 用 的 代 码 简 练 高 效, 即 所 谓 的“ 瘦 代 码”(Slim Code)。ATL 在 实 现 上 尽 可 能 采 用 优 化 技 术, 甚 至 在 其 内 部 提 供 了 所 有C/C++ 开 发 的 程 序 所 必 须 具 有 的C 启 动 代 码 的 替 代 部 分。 同 时ATL 产 生 的 代 码 在 运 行 时 不 需 要 依 赖 于 类 似MFC 程 序 所 需 要 的 庞 大 的 代 码 模 块, 包 含 在 最 终 模 块 中 的 功 能 是 用 户 认 为 最 基 本 和 最 必 需 的。 这 些 措 施 使 采 用ATL 开 发 的COM 组 件( 包 括ActiveX Control) 可 以 在 网 络 环 境 下 实 现 应 用 的 分 布 式 组 件 结 构。
---- 第 三,ATL 的 各 个 版 本 对Microsoft 的 基 于COM 的 各 种 新 的 组 件 技 术 如MTS、ASP 等 都 有 很 好 的 支 持,ATL 对 新 技 术 的 反 应 速 度 大 大 快 于MFC。ATL 已 经 成 为Microsoft 支 持COM 应 用 开 发 的 主 要 开 发 工 具, 因 此COM 技 术 方 面 的 新 进 展 在 很 短 的 时 间 内 都 会 在ATL 中 得 到 反 映。 这 使 开 发 者 使 用ATL 进 行COM 编 程 可 以 得 到 与 直 接 使 用COM SDK 编 程 同 样 的 灵 活 性 和 强 大 的 功 能。
---- 本 文 的 目 的 就 是 希 望 在 有 限 的 篇 幅 中 能 够 使 读 者 对ATL 的 使 用 和 基 本 原 理 有 一 个 初 步 的 了 解, 为 广 大 的COM 开 发 人 员 更 好 地 使 用ATL 开 发 起 到 抛 砖 引 玉 的 作 用。
二、ATL 基 本 技 术
---- 虽 然 使 用ATL 开 发COM 应 用 是 一 件 非 常 简 单 的 事 情, 但 是 在ATL 简 单 易 用 的 界 面 后 面 却 包 含 着 复 杂 的 技 术。 面 对ATL 生 成 的 大 量 代 码, 我 们 即 使 不 去 深 入 地 了 解 这 些 代 码 的 含 义 也 可 以 开 发 出COM 应 用 来, 但 是 如 果 我 们 要 充 分 地 挖 掘ATL 的 潜 力, 开 发 出 更 灵 活、 强 大 的COM 应 用, 则 必 须 对ATL 使 用 的 基 本 技 术 有 所 了 解。 研 究ATL 的 实 质 最 好 的 教 材 就 是 由Visual C++ 提 供 的ATL 源 代 码。 本 文 这 一 部 分 只 是 对ATL 中 用 到 的 最 基 本 的 技 术 进 行 简 单 的 介 绍。
---- 简 单 地 说 来,ATL 中 所 使 用 的 基 本 技 术 包 括 以 下 几 个 方 面:
- COM 技 术
- C++ 模 板 类 技 术(Template)
- C++ 多 继 承 技 术(Multi-Inheritance)
---- COM 技 术 是 理 解ATL 的 基 础, 使 用ATL 进 行 开 发 要 对COM 技 术 的 基 本 概 念 有 最 低 限 度 的 了 解。 由 于COM 是 一 项 非 常 复 杂 庞 大 的 技 术 体 系, 限 于 本 文 的 篇 幅, 这 里 不 再 赘 述。 对 于 本 文 中 提 到 的COM 基 本 概 念 也 不 做 过 多 的 解 释, 请 读 者 参 阅 有 关 的 参 考 书 籍。 作 为ATL 最 核 心 的 实 现 技 术 的 模 板 是 对 标 准C++ 语 言 的 扩 展, 但 是 在 大 多 数 的C++ 编 程 环 境 中, 人 们 很 少 使 用 它, 这 是 因 为 模 板 的 功 能 虽 然 很 强, 但 是 它 内 部 机 制 比 较 复 杂, 需 要 比 较 多 的C++ 知 识 和 经 验 才 能 灵 活 地 使 用 它。 在MFC 中 的CObjectArray 等 功 能 类 就 是 由 模 板 来 定 义 的。 完 全 通 过 模 板 来 定 义 程 序 的 整 体 类 结 构,ATL 是 迄 今 为 止 做 得 最 为 成 功 的。
---- 所 谓 模 板 类 简 单 地 说 是 对 类 的 抽 象。 我 们 知 道C++ 语 言 用 类 定 义 了 构 造 对 象( 这 里 指C++ 对 象 而 不 是COM 对 象) 的 方 式, 对 象 是 类 的 实 例, 而 模 板 类 定 义 的 是 类 的 构 造 方 式, 使 用 模 板 类 定 义 实 例 化 的 结 果 产 生 的 是 不 同 的 类。 因 此 可 以 说 模 板 类 是“ 类 的 类”。 ( 未 完 待 续)
---- ( 接 上 期)
---- 在C++ 语 言 中 模 板 类 的 定 义 格 式 如 下:
template < class T > class MyTemp { MyTemp< T >( ) { }; ~MyTemp< T >( ) { }; int MyFunc( int a) ; } … … Int MyTemp< T >::MyFunc( int a) { }
---- 首 先 使 用C++ 的 关 键 字“template” 来 声 明 一 个 模 板 类 的 定 义。 在 关 键 字 后 面 是 用 尖 括 号 括 起 来 的 类 型 参 数。 正 是 根 据 这 个 类 型 参 数, 编 译 器 才 能 在 编 译 过 程 中 将 模 板 类 的 具 体 定 义 转 化 为 一 个 实 际 的 类 的 定 义, 即 生 成 一 个 新 类。 接 下 来 的 定 义 方 式 与 普 通 的 类 定 义 十 分 相 似, 只 是 在 类 的 函 数 定 义 中 都 要 带 有 类 型 参 数 的 说 明。
---- 下 面 的 程 序 段 说 明 了 模 板 类 的 用 法:
typedef MyTemp< MyClass > myclassfromtemp; myclassfromtemp m; int a = m.Myfunc(10);
---- 通 常 在 使 用 模 板 类 时 为 了 方 便 起 见, 使 用 一 个 关 键 字“typedef” 为 新 定 义 出 来 的 类 取 一 个 名 字。 在 上 面 的 程 序 段 中 假 设“MyClass” 是 一 个 由 用 户 定 义 的 类, 通 过 将 这 个 类 的 名 字 作 为 类 型 参 数 传 递 给 模 板 类, 我 们 可 以 创 建 一 个 新 的 类, 这 个 类 的 行 为 将 以 模 板 类 的 定 义 为 基 础, 例 如 它 具 有 模 板 类 定 义 的 所 有 成 员 函 数, 同 时 这 个 类 又 是 对 模 板 类 行 为 的 一 种 修 改, 这 种 修 改 是 通 过 用 户 提 供 的 类 型 参 数 来 实 现 的。 赋 予 模 板 类 以 不 同 的 类 型 参 数, 则 得 到 行 为 框 架 相 似 但 具 体 行 为 不 同 的 一 组 类 的 集 合。 有 了 新 的 类 的 定 义 以 后, 我 们 可 以 像 使 用 普 通 类 一 样 来 创 建 一 个 类 的 实 例, 即 一 个 新 的 对 象, 并 且 调 用 这 个 对 象 的 成 员 函 数。
---- 模 板 类 是 对 标 准C++ 语 言 的 最 新 扩 展, 虽 然 它 的 功 能 很 强 大, 但 是 要 想 使 用 好 模 板 类 需 要 相 当 多 的 关 于 语 言 和 编 程 的 经 验 和 知 识, 而 且 错 误 地 使 用 模 板 类 又 会 对 程 序 的 结 构 和 运 行 效 率 带 来 很 大 的 副 作 用, 因 此 一 般 的 编 程 环 境 和 编 程 书 籍 对 模 板 类 的 使 用 都 采 取 谨 慎 的 态 度。 而ATL 的 核 心 就 是 由 几 十 个 模 板 类 构 成 的, 通 过 研 究ATL 的 源 代 码 可 以 使 我 们 对 模 板 类 的 使 用 有 比 较 全 面 的 认 识。
---- 多 继 承 技 术 同 模 板 一 样, 是C++ 语 言 中 极 具 争 议 性 的 技 术。 使 用 多 继 承 技 术 可 以 使 程 序 的 设 计 和 实 现 更 加 灵 活, 但 是, 由 于 多 继 承 的 复 杂 性 和 自 身 概 念 上 的 一 些 问 题, 使 多 继 承 在 各 种 面 向 对 象 的 语 言 环 境 中 得 到 的 支 持 都 非 常 有 限。 例 如Small Talk 根 本 就 不 允 许 多 继 承, 同 样MFC 也 不 支 持 多 继 承 技 术。
---- 多 继 承 最 大 的 问 题 是 所 谓 的“ 钻 石 结 构”。 例 如 下 面 的 代 码:
class A { ...... }; class B : public A { ...... }; class C : public A { ...... }; class D : public C,B { ...... }
---- 由 于 类D 同 时 从 类C 和 类B 继 承, 因 此 在 下 面 的 语 句 中 就 会 发 生 歧 义:
D* pD = new D; (A*)pD- >Func(...);
---- 由 于 类D 通 过 类C 和 类B 分 别 继 承 了 类A, 这 里 的 强 制 转 化 就 会 发 生 歧 义。
---- ATL 使 用 了C++ 最 新 规 范 中 加 入 的 两 个 运 算 符 号 static_cast、dynamic_cast 代 替 简 单 的 强 制 转 化, 从 而 消 除 多 继 承 带 来 的 歧 义。 使 用 这 两 个 运 算 符 号, 我 们 可 以 在 对 象 运 行 过 程 中 获 取 对 象 的 类 型 信 息。 上 面 的 代 码 可 以 采 用 下 面 的 方 式 修 改:
D* pD = new D; static_cast< A* >(static_cast< B* >(pD))- >Func(...);
---- 为 什 么 模 板 类 和 多 继 承 技 术 会 成 为ATL 主 要 的 工 具 呢 ? 原 因 在 于, 采 用 模 板 可 以 在 编 译 过 程 中 快 速 生 成 具 有 用 户 定 制 功 能 的 类, 这 对 于COM 这 样 一 个 复 杂 的 技 术 体 系 在 实 现 效 率 上 得 到 了 很 大 的 提 高。 通 过 使 用 模 板 类, 用 户 可 以 把 精 力 集 中 在 自 己 开 发 的 类 的 基 本 逻 辑 上, 在 完 成 了 自 己 的 类 的 设 计 以 后, 通 过 继 承 不 同 的 类, 生 成 不 同 的 模 板 类, 就 可 以 快 速 实 现COM 的 功 能, 同 时 又 避 免 了 采 用 单 继 承 结 构 造 成 的 大 量 功 能 冗 余。
---- 总 之, 正 是 由 于 在 设 计 实 现 过 程 中 采 用 了 模 板 类 和 多 继 承 技 术, 才 使ATL 成 为 一 个 小 巧 灵 活 的COM 开 发 工 具, 能 够 适 应 开 发 人 员 对COM 应 用 开 发 的 各 种 需 要。
三、ATL 基 本 使 用
---- 这 一 部 分 将 重 点 介 绍ATL 的 基 本 使 用 过 程。 由 于ATL 已 经 被 集 成 在Microsoft Visual Studio 的Visual C++ 开 发 环 境 中, 因 此 要 使 用ATL 必 须 先 安 装Visual C++。 在 下 面 的 讨 论 中 有 关COM 的 基 本 知 识 请 参 阅 有 关 的 文 档, 这 里 不 再 详 细 说 明。 给 出 的 图 是 在Microsoft Windows 98 平 台 下Visual Studio 6.0 的 使 用 示 意 图。
---- 使 用ATL 开 发 一 个COM 应 用 基 本 可 以 分 为 以 下 几 个 步 骤:
- 创 建 一 个 新 的ATL 工 程, 并 对 工 程 的 选 项 进 行 适 当 的 配 置。
- 向 新 创 建 的 工 程 添 加 新 的ATL 类, 并 对 该 类 进 行 一 些 初 始 配 置 工 作。
- 根 据COM 应 用 的 基 本 要 求 向 新 的ATL 类 加 入 新 的 接 口 定 义, 并 实 现 相 应 的 接 口 成 员 函 数。
- 编 译 连 接 工 程, 注 册COM 应 用。
下 面 将 根 据 这 些 步 骤 依 次 介 绍ATL 的 基 本 使 用 过 程。
---- 1. 创 建 工 程
---- 首 先 启 动Visual C++ 集 成 开 发 环 境, 选 择“File” 菜 单 下 的“New...” 命 令, 在“New” 对 话 框 中 选 择“Project” 页, 如 图1 所 示。
图1 创 建 新 工 程 界 面 示 意 图
---- 选 择“ATL COM AppWizard” 项, 这 是 创 建ATL 工 程 的AppWizard 向 导 入 口。 然 后 在“Project name” 编 辑 框 中 输 入 工 程 的 名 字, 单 击“OK” 按 钮, 进 入AppWizard 对 话 框。 如 图2 所 示。
图2 ATL COM AppWizard 对 话 框 示 意 图
---- 在AppWizard 对 话 框 中 主 要 的 设 置 选 项 有:
---- ■ COM 服 务 程 序 的 类 型:
---- 动 态 连 接 库(Dynamic Linking Library) 最 终 产 生 一 个 动 态 连 接 库(DLL) 形 式 的COM 服 务 程 序;
---- 应 用 程 序(Executable application) 最 终 产 生 一 个 可 执 行 程 序 类 型(EXE) 的COM 服 务 程 序;
---- NT 服 务(NT Service) 产 生 一 个 以NT 服 务 方 式 运 行 的COM 服 务 程 序。
---- ■ 允 许 嵌 入Proxy/Stub 代 码。 由Microsoft 提 供 的MIDL 编 译IDL 文 件 以 后, 将 产 生 用 于 对 象 调 度(Marshaling) 的Proxy/Stub 的 代 码。 在 传 统 方 式 下, 这 部 分 代 码 与COM 服 务 程 序 的 代 码 是 分 离 的, 但 是 由 于 新 的COM 标 准 支 持 多 线 程 环 境 下 的COM 对 象 服 务, 因 此 在 动 态 连 接 库 的COM 服 务 程 序 中 也 要 有Proxy/Stub 的 支 持。 为 了 支 持 在 网 络 上 的 传 输,ATL 允 许 用 户 选 择 将Proxy/Stub 的 代 码 包 括 在 生 成 的DLL 代 码 中。 这 个 选 项 在EXE 和NT 服 务 类 型 的COM 应 用 条 件 下 不 可 选。 ◎( 未 完 待 续)
---- ( 接 上 期)
- 允 许 支 持MFC。 由 于ATL 对 除COM 以 外 的 基 本 的Windows 编 程 方 面 的 支 持 极 为 有 限, 同 时 许 多 程 序 员 对MFC 又 非 常 熟 悉, 因 此 在ATL 的 工 程 设 置 中 允 许 在ATL 工 程 内 部 支 持 使 用MFC, 即 可 以 使 用MFC 定 义 的 类。 这 一 特 点 给 开 发 人 员 提 供 了 许 多 方 便, 特 别 对 于 习 惯 使 用MFC 的 人 来 说, 能 够 使 用MFC 提 供 的 各 种 功 能 强 大 的 类 的 支 持, 而 不 必 直 接 使 用Windows SDK。 从 另 一 个 方 面 来 看, 在ATL 工 程 中 使 用MFC 同 时 就 丧 失 了ATL 代 码 轻 量 级 的 特 点。
- 支 持MTS。MTS 是Microsoft Transaction Server 的 缩 写, 它 是Microsoft 在COM 技 术 方 面 的 一 个 新 的 分 支, 这 里 不 做 详 细 说 明。
---- 完 成 上 面 的 设 置 以 后, 可 以 选 择Finish 完 成 工 程 的 设 置,ATL 将 创 建 相 应 的 工 程。
---- 2 . 加 入ATL 类
---- 完 成 工 程 的 创 建 和 设 置 以 后, 下 一 步 就 是 向 工 程 中 加 入 一 个 新 的ATL 类。Visual Studio 集 成 环 境 提 供 了 向 导 工 具"ATL Object Wizard" 用 于 加 入 一 个 新 的ATL 类。 操 作 过 程 并 不 复 杂, 只 是 一 组 对 话 框 操 作 而 已。
---- 首 先 通 过 集 成 环 境 的"Insert" 菜 单 下 的"New ATL Object..." 命 令 进 入"ATL Object Wizard" 对 话 框, 如 图1 所 示。
图1 ATL Object Wizard 对 话 框 示 意 图
---- 这 个 对 话 框 即 为 创 建ATL 对 象 的 向 导 起 始 界 面。 对 话 框 的 左 边 部 分 说 明 了 待 创 建 对 象 的 基 本 类 型, 这 里 主 要 有 以 下 几 种 类 型:
- 对 象(Object) 基 本 的COM 对 象 类 型;
- 控 制(Control) ActiveX Control 类 型 的ATL 对 象;
- 其 他(Miscellaneous) 辅 助 功 能, 如 对 话 框 的 生 成 等;
- 数 据 访 问(Data Access) 数 据 访 问, 支 持MTS 等。
---- 右 边 部 分 说 明 了 每 种 类 型 的 详 细 内 容。 对 于 一 般 的COM 服 务 程 序, 使 用 对 象 表 中 的 简 单 对 象(Simple Object) 就 可 以 了。
---- 选 定 待 创 建 对 象 的 基 本 类 型 以 后, 单 击"Next" 按 钮 进 入 下 一 步, 进 入 对 象 属 性 设 置 对 话 框。
---- 对 象 属 性 设 置 分 为 两 个 过 程: 先 是 对 象 名 字 标 识 的 设 定, 然 后 是 对 对 象 的 基 本 属 性 进 行 设 置。 首 先 是 对 象 的 名 字 标 识 设 置, 如 图2 所 示。
图2 对 象 名 字 标 识 设 置 对 话 框 示 意 图
---- 在 对 象 标 识 编 辑 框 中 输 入 待 创 建 对 象 的 名 字,ATL 对 象 向 导 将 同 步 地 根 据 用 户 输 入 的 对 象 标 识 设 定 该 对 象 的C++ 标 识 和COM 标 识。 对 象 的C++ 标 识 包 括 对 象 的 类 名、cpp 文 件 名 和 头 文 件 名。COM 标 识 包 括 对 象 在 类 型 库 中 的CoClass 段 和 实 现 的 主 接 口 的 名 字, 同 时 还 有 在 系 统 注 册 表 中 的 类 型 名 以 及ProgID。
---- 对 象 名 字 标 识 设 置 完 成 以 后, 选 择 对 象 属 性 页(Attribute) 进 入 对 象 的 属 性 设 置 页 面, 如 图3 所 示。
图3 对 象 属 性 设 置 对 话 框 示 意 图
---- 对 象 的 属 性 设 置 是ATL 对 象 创 建 过 程 中 最 复 杂 的 部 分, 包 括 以 下 几 个 主 要 部 分:
- 对 象 的 线 程 模 型(Thread Model)
对 象 的 线 程 模 型 是COM 对 象 在 多 线 程 环 境 下 被 访 问 时 对 访 问 方 式 的 控 制, 缺 省 情 况 下 在ATL 中 采 用 的 是 套 间 模 型Apartment, 由 系 统 通 过 消 息 队 列 方 式 提 供 并 发 控 制。
- 对 象 的 接 口 模 型(Interface)
COM 对 象 的 接 口 可 以 是 双 接 口(Dual Interface)。 双 接 口 不 同 于 普 通 接 口(Custom Interface) 之 处 在 于 双 接 口 是 从Automation 基 本 接 口IDispatch 继 承 的, 而 普 通 接 口 是 从IUnknown 接 口 直 接 继 承 来 的。 缺 省 的 接 口 模 型 是 双 接 口。
- 对 象 的 聚 合 模 型(Aggregate)
COM 规 范 不 允 许 对 象 的 实 现 继 承, 但 是 可 以 通 过 聚 合 方 式 重 用 其 他 的COM 对 象。ATL 对 象 属 性 设 置 中 的 聚 合 模 型 可 以 指 定 待 创 建 的COM 对 象 是 否 支 持 聚 合 模 型。 缺 省 的 选 项 是 支 持 对 象 的 聚 合。
- 对 象 对 错 误 处 理 的 支 持(Support ISupportErrorInfo)
选 取 这 个 选 项 可 以 在 对 象 的 运 行 过 程 中 支 持 错 误 处 理。 缺 省 情 况 下 这 个 选 项 不 被 选 中。
- 对 象 对 连 接 点 的 支 持(Support Connection Points)
连 接 点 是COM 对 象 的 事 件 机 制。 选 中 这 个 选 项 可 以 使 待 创 建 的COM 对 象 具 有 发 出 事 件 的 能 力。 缺 省 情 况 下 该 选 项 不 被 选 中。
- 对 象 对 自 由 线 程 调 度 的 支 持(Free Thread Marshaller, 简 称FTM)
对 象 的 自 由 线 程 调 度 是 对 象 在 处 于 自 由 线 程 模 型 状 态 下, 为 了 简 化 对 象 的 访 问 过 程 而 采 用 的 一 种 优 化 策 略。 缺 省 情 况 下 该 选 项 不 被 选 中。
---- 对 于 上 述 的 任 何 一 个 选 项 的 详 细 描 述 都 涉 及 到COM 技 术 一 些 核 心 的 内 容, 并 且 都 已 超 出 本 文 的 范 围, 因 此 本 文 只 对ATL 给 出 的 缺 省 选 项 加 以 说 明, 对 这 些 内 容 感 兴 趣 的 读 者 可 以 参 考Microsoft 提 供 的 文 档。
---- 完 成 了 上 面 的 设 置 以 后, 就 可 以 按"OK" 按 钮 完 成 对 象 的 创 建 过 程。 下 一 步 就 是 向 所 生 成 的ATL 类 的 接 口 中 加 入 成 员 函 数 的 定 义, 以 及 接 口 成 员 函 数 的 实 现 过 程。
---- 3. 加 入 接 口 定 义, 实 现 接 口 函 数
---- 加 入 了ATL 类 定 义 之 后, 我 们 可 以 打 开Visual C++ 集 成 环 境 下 项 目 管 理 器(Workspace) 中 的Class View 来 检 查 生 成 的 类 定 义 的 情 况( 见 图4)。 我 们 可 以 看 到 一 个 新 的 类 已 经 生 成, 同 时 还 生 成 了 相 应 的 接 口 定 义。ATL Object Wizard 为 我 们 生 成 了 类 定 义 的.h 和.cpp 文 件, 此 外 还 有 用 于 接 口 定 义 的IDL 文 件。 有 了 这 些 文 件 以 后, 我 们 就 可 以 为 接 口 加 入 成 员 函 数, 完 成 类 的 定 义。
图4 ATL 工 程 的ClassView 示 意 图
---- 首 先 在Class View 中 选 中 相 应 的 接 口, 图4 中 显 示 为 接 口IATLTest, 单 击 鼠 标 右 键 打 开 菜 单, 如 图5。 此 弹 出 式 菜 单 定 义 了 为 接 口 加 入 属 性 和 方 法 的 操 作。 选 取 其 中 的"Add Method" 项 可 以 为 接 口 加 入 方 法 成 员, 选 取"Add Property" 则 可 以 为 接 口 加 入 新 的 属 性 成 员。
图5 接 口 编 辑 菜 单 示 意 图
---- 加 入 属 性 和 方 法 的 对 话 框 可 以 参 看 图6 和 图7。 如 果 我 们 要 在 接 口 中 加 入 一 个 方 法, 则 选 取"Add Method" 菜 单 命 令。 假 设 方 法 名 为ABC, 方 法 的 返 回 类 型 为COM 规 定 的HRESULT 类 型。 我 们 也 可 以 定 义 非HRESULT 返 回 类 型 的 函 数, 但 是 这 需 要 手 工 修 改 接 口 定 义 的IDL 文 件。 我 们 定 义ABC 方 法 的 一 个 参 数 为a, 类 型 为 整 数 型。 完 成 了 方 法 的 定 义 以 后, 单 击"OK" 按 钮 则 把 此 方 法 加 入 到 接 口 中。
图6 加 入 接 口 方 法 的 界 面
图7 加 入 接 口 属 性 的 界 面
---- 属 性 的 加 入 过 程 是 类 似 的。 属 性 加 入 对 话 框 要 求 指 定 属 性 的 类 型、 名 字 以 及 属 性 的 访 问 方 式。 在 属 性 和 方 法 的 编 辑 对 话 框 中 都 有 一 个"Attributes" 按 钮, 在 给 出 了 一 个 属 性 或 方 法 的 基 本 定 义 之 后, 单 击 此 按 钮, 可 以 对 属 性 和 方 法 的 一 些 高 级 特 性 进 行 设 置。
---- 方 法 成 员 加 入 以 后, 我 们 可 以 通 过Class View 来 检 查ATL 为 我 们 所 做 的 工 作, 如 图8 所 示。 首 先 我 们 看 到ATL 在 接 口 的 定 义 中 加 入 了 该 方 法 的 定 义, 同 时 在 对 应 的ATL 类 定 义 中, 也 加 入 了 一 个 相 应 的 方 法 的 定 义, 在 类 对 应 的.cpp 文 件 中, 加 入 了 此 方 法 的 实 现 框 架。 此 后, 我 们 只 要 在 这 个 函 数 框 架 中 加 入 该 方 法 的 代 码 逻 辑, 一 个 接 口 函 数 的 定 义 和 实 现 就 基 本 完 成 了。 依 照 这 种 方 式, 我 们 可 以 完 成 整 个COM 对 象 的 定 义 和 实 现。
图8 加 入 接 口 方 法 以 后 的Class View
---- 完 成 以 上 的 步 骤 之 后, 我 们 就 可 以 编 译 连 接 应 用 了。( 未 完 待 续)
(接 上 期)
---- 4. 编 译 连 接 应 用、 注 册COM 服 务 程 序
---- 对ATL 工 程 的 编 译 连 接 过 程 包 括 下 面 的 几 个 步 骤:
- 使 用MIDL 编 译 工 程 的IDL 文 件, 形 成 接 口 定 义 的 头 文 件 和 用 于 调 度(Marshalling) 的 代 码。
- 编 译 工 程 的.cpp 文 件 形 成 目 标 文 件。
- 连 接 目 标 文 件, 形 成 应 用 模 块。
- 注 册COM 服 务 程 序。
---- 关 于 工 程 编 译 连 接 的 其 他 部 分 同Visual C++ 中MFC 工 程 的 编 译 连 接 过 程 相 似, 这 里 只 重 点 介 绍 一 下COM 服 务 程 序 的 注 册 过 程。
---- 在ATL 中,COM 服 务 程 序 的 注 册 是 在 工 程 编 译 连 接 的 最 后 阶 段, 由ATL 辅 助 完 成 的。 在 手 工 的COM 编 程 中, 服 务 程 序 的 注 册 是 比 较 麻 烦 的 工 作。 在ATL 中, 系 统 通 过 读 取 在 建 立 工 程 过 程 中 形 成 的 注 册 脚 本 文 件 来 完 成 注 册 工 作。 注 册 脚 本(Register Script, 简 称RGS) 是ATL 提 供 的 文 本 方 式 的 注 册 辅 助 文 件。 下 面 是 注 册 脚 本 文 件 的 一 个 实 例。
---- HKCR - 表 示 注 册 表 中COM 对 象 的 注 册 项, 是HKEY_CLASS_ROOT 的 缩 写
{ AuthTest.ActiveXObject.1 = s'ActiveXObject Class' { CLSID = s '{97A5CB10-AF82-11D2-B9BC-00104B04B867}' } - 对 象 的ProgID AuthTest.ActiveXObject = s 'ActiveXObject Class' { CLSID = s '{97A5CB10-AF82-11D2-B9BC-00104B04B867}' } - 对 象 的 与 版 本 无 关 的ProgID NoRemove CLSID - 对 象CLSID 注 册 项 { ForceRemove {97A5CB10-AF82-11D2-B9BC-00104B04B867} = s 'ActiveXObject Class' { ProgID = s 'AuthTest.ActiveXObject.1' VersionIndependentProgID = s 'AuthTest.ActiveXObject' InprocServer32 = s '%MODULE% ' - 服 务 器 类 型, 表 示DLL 服 务 器 { val ThreadingModel = s 'both' - 线 程 模 型, 这 里 是BOTH 型 } } } }
---- RGS 文 件 包 含 注 册COM 服 务 程 序 的 各 项 内 容, 通 常 我 们 不 必 修 改 此RGS 文 件, 必 要 时 我 们 也 可 以 手 工 修 改RGS 文 件 来 定 制 模 块 的 注 册 过 程。
四、 应 用ATL 的 一 个 例 子
---- 上 面 介 绍 了 使 用ATL 创 建 一 个COM 服 务 程 序 的 基 本 过 程。 在 介 绍 过 程 中, 我 们 实 际 上 已 经 生 成 了 一 个COM 服 务 程 序 的 基 本 框 架, 只 是 没 有 填 写 实 际 的 内 容。 在 下 面 部 分, 我 们 将 具 体 开 发 一 个 十 分 简 单 的COM 服 务 程 序, 并 且 为 它 编 写 一 段 客 户 代 码 进 行 测 试, 使 大 家 对 使 用ATL 开 发COM 服 务 程 序 的 过 程 有 一 个 全 面 整 体 的 了 解。
---- 我 们 要 开 发 的 服 务 程 序 的 功 能 很 简 单, 它 只 实 现 一 个 接 口, 这 个 接 口 名 字 是ISimpleInterface, 接 口 只 有 一 个 成 员 函 数, 叫 做Welcome。 这 个 函 数 的 功 能 只 是 输 出 一 个"Hello World!" 的 字 符 串。
---- 按 照 上 一 部 分 介 绍 的 创 建COM 服 务 程 序 的 步 骤, 我 们 进 行 如 下 的 操 作。
- 打 开Visual C++ 集 成 开 发 环 境。
- 创 建 一 个 称 为SimpleTest 的ATL 工 程。
- 在 这 个 工 程 中 插 入 新 的 对 象, 对 象 的 名 字 是SimpleInterface。
- 设 置 接 口ISimpleInterface 的 有 关 属 性, 使 它 成 为 一 个 双 接 口。
- 在 对 象 的 接 口ISimpleInterface 中 加 入 方 法Welcome。
- 打 开ATL 加 入 的Welcome 方 法 的 框 架, 可 以 看 到 如 下 的 代 码 段。
STDMETHODIMP CActiveXObject::get_TestProp(long *pVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) // TODO: Add your implementation code here return S_OK; }
---- 7 . 将 程 序 框 架 中 的 注 释 部 分 替 换 为 下 面 的 代 码:
::MessageBox(NULL,_T("Hello World!"),_T("Welcome"), MB_OK)。 Welcome 方 法 被 调 用 时 将 弹 出 一 个 消 息 框。
---- 8 . 编 译 连 接 工 程。
---- 上 面 的 步 骤 完 成 以 后, 我 们 就 有 了 一 个 简 单 的COM 服 务 程 序, 而 且 已 经 被 注 册 到 当 前 系 统 中。
---- 下 面 我 们 要 完 成 一 个 简 单 的COM 客 户 程 序。 一 个COM 客 户 程 序 简 单 地 说 是 使 用COM 组 件 对 象 的 程 序。 客 户 程 序 调 用COM 对 象 的 基 本 流 程 如 下。
- 创 建COM 对 象 的 实 例。 这 可 以 通 过 调 用Windows 系 统 的API 函 数CoCreateInstance 来 完 成。
- 通 过 接 口 调 用 函 数。
- 调 用IUnknown::Release 释 放COM 对 象 实 例。
---- 我 们 的 客 户 程 序 是 使 用MFC 编 写 的 一 个 基 于 对 话 框 的 简 单 应 用 程 序。 具 体 的 过 程 如 下:
- 打 开Visual C++ 集 成 开 发 环 境。
- 创 建 一 个 称 为SimpleClient 的 基 于 对 话 框 的MFC 工 程。
- 在 对 话 框 中 加 入 一 个 按 钮, 名 字 为TEST。
- 在SimpleClient.cpp 文 件 中 加 入 如 下 的 代 码。
---- (1) 在.cpp 文 件 #include "simpleclientdlg.h" 之 后 加 入 下 面 的 代 码:
#include "d:/simpletest/simpletest_i.h" // 根 据 需 要 修 改 头 文 件 的 路 径
---- 加 入 的 头 文 件 是 在 编 译COM 服 务 程 序 过 程 中 自 动 生 成 的, 其 中 包 含 接 口 本 身 的 定 义、 接 口IID 的 定 义 和COM 对 象 的CLSID 的 定 义。 包 含 该 头 文 件 可 以 使 客 户 程 序 能 够 使 用COM 服 务 程 序。
---- (2) 在 按 钮TEST 的 消 息 控 制 函 数 中 加 入 如 下 的 代 码:
HRESULT hr; ISimpleInterface* pIntf = NULL; hr = CoCreateInstance (CLSID_SimpleInterface, NULL, CLSCTX_SERVER , IID_ISimpleInterface, (void **)& pIntf); if(SUCCEEDED(hr)) { pIntf- >Welcome(); pIntf- >Release(); }
---- 上 面 的 代 码 首 先 通 过 系 统API CoCreateInstance 创 建COM 对 象, 得 到 接 口 的 指 针, 然 后 调 用 接 口 成 员 函 数Welcome, 最 后 通 过IUnknown::Release() 函 数 释 放COM 对 象 实 例。
---- 5 . 编 译 连 接 客 户 程 序。
---- 最 后, 我 们 可 以 测 试 客 户 程 序 是 否 正 常 运 行。 启 动 客 户 程 序, 当 单 击"TEST" 按 钮 时 我 们 可 以 看 到 弹 出 一 个 消 息 框, 这 正 是 我 们 的COM 服 务 程 序 提 供 的 功 能。 ( 全 文 完)