1、使 用MFC 开 发ISAPI Extensions 程 序使 用MFC 开 发ISAPI Extensions 程 序 引 言- 1 1 ISAPI 与CGI - 通 用 网 关 接 口Common Gateway Interface (CGI) 很 早 就 作 为 交 互 式 的Web 应 用 程 序 的 一 个 标 准 广 泛 应 用 在Internet 之 中。CGI 脚 本 允 许 人 们 用 多 种 编 程 语 言( 如Basic、C、Perl、Shell 等 等) 来 编 写 简 单 的 应 用 程 序。 这 些 脚 本 运 行 在Web 服 务 器 上, 而 在 客 户 的We
2、b 浏 览 器 上 输 出 运 行 结 果。 客 户 的 输 入 通 过 环 境 变 量 或 者 标 准 输 入 设 备 来 进 行 传 递, 然 后CGI 程 序 根 据 需 要 完 成 特 定 的 功 能, 并 通 过 标 准 输 出 设 备 送 回HTML 格 式 的 结 果 显 示 在 客 户 的 浏 览 器 中。CGI 的 这 一 特 性 设 计 简 单, 再 加 上 它 支 持 多 种 编 程 语 言, 使 得 开 发CGI 应 用 程 序 非 常 简 单。 尽 管 如 此, 人 们 在 使 用 中 还 是 发 现 了CGI 应 用 程 序 的 一 个 很 大 的 缺 点: 性 能
3、 不 高。 虽 然 有 不 少 办 法 来 使CGI 应 用 程 序 运 行 得 更 快 一 些( 如 把 它 们 变 成 编 译 好 的 二 进 制 代 码, 而 不 用Perl 脚 本), 但 执 行 速 度 仍 然 是 一 个 问 题。 每 当 通 过Web 访 问 一 个CGI 程 序 时,CGI 执 行 文 件( 或 者 脚 本 的 解 释 器) 都 要 为 每 一 个 请 求 创 建 一 个 新 的 进 程。 对 于 一 个 信 息 量 比 较 大 的 站 点 来 说, 这 无 疑 给 服 务 器 增 加 了 一 个 沉 重 的 负 担。 - 当 微 软 开 始 开 发 自 己 的
4、Web 服 务 器(Microsoft Internet Information Server 或 简 称 为IIS) 时, 意 识 到 了CGI 的 这 一 大 的 缺 陷, 于 是 他 们 就 引 入 了ISAPI。 - ISAPI 使 用 动 态 链 接 库DLL 而 不 是 可 执 行 代 码。 这 些DLLs 被 装 入 到 服 务 器 的 内 存 空 间。 由 于 代 码 在 内 存 中 缓 存 起 来 了, 而 不 是 每 次 接 收 到 请 求 时 再 装 入 到 内 存 中, 因 此 这 一 技 术 极 大 地 提 高 了 交 互 式 的Web 应 用 程 序 的 性 能。
5、- ISAPI 程 序 分 为 两 种: 一 种 就 是 我 们 要 介 绍 的ISAPI Extensions, 它 提 供 了 一 种 比CGI 更 好 的 实 现 方 法; 另 一 种 称 作ISAPI Filter, 它 可 以 对 服 务 器 上 进 入 或 出 去 的 数 据 进 行 过 滤。 - 总 的 来 说,ISAPI 优 于CGI 之 处 包 括: - 速 度 快:ISAPI 在 性 能 上 有 很 大 的 提 高; - 功 能 强:ISAPI 还 可 以 创 建Filter 以 对 数 据 进 行 预 处 理。 并 且 它 完 全 与MFC 集 成 在 一 起 了。 -
6、相 反,ISAPI 的 不 足 之 处 有: - 标 准 化 不 够: 目 前 只 有 一 部 分 服 务 器 支 持ISAPI; - 开 发 困 难: 相 关 资 料 很 少, 并 且 调 试 很 麻 烦。 - 1 2 ISAPI 基 础 - ISAs 开 发 主 要 基 于MFC 的CHttpServer 类。 该 类 控 制 了 所 有 与 服 务 器 的 交 互 操 作, 同 时 还 包 含 了 用 于 客 户 请 求 的 所 有 函 数。 尽 管 一 个ISA 只 能 有CHttpServer 类 的 一 个 实 例, 但每 个ISA 仍 然 可 以 同 时 处 理 多 个 请 求。
7、 这 是 通 过CHttpServer 类 为 每 个 请 求 创 建 一 个CHttpServerContext 类 来 实 现 的。CHttpServerContext 类 包 含 了 每 个 特 定 请 求 的 所 有 数 据 以 及 由ISA 返 回 到 客 户 的 所 有HTML 代 码。 - ISAPI DLLs 的 调 用 方 法 和CGI 一 样: 在 客 户 端 使 用GET 或POST 方 法。 例 如, 当 客 户 作 出 下 列 请 求 时: - - name 域 和id 域 以 及 与 它 们 相 关 的 数 据 都 被 传 递 给ISA。ISAPI 在 使 用 这
8、些 相 关 的 数 据 之 前 把 它 们 存 放 在 相 应 的 数 据 结 构 中, 这 是 通 过 一 个 请 求 映 射 系 统 来 实 现 的。 - 每 一 个 请 求 都 有 一 个 解 析 映 射 表。 通 过 定 义 服 务 器 从 客 户 接 收 的 数 据 的 类 型 和 顺 序, 该 解 析 映 射 表 可 以 把 数 据 传 递 到 合 适 的 数 据 结 构 中。 例 如, 对 于 请 求name=fisherman&id=12345, 解 析 映 射 表 将 显 示 一 个 字 符 串 和 一 个 整 型 数, 并 且 fisherman and 12345 将 被
9、 解 析 出 来 存 放 到 各 自 的 数 据 结 构 中。 - 解 析 映 射 系 统 还 有 另 外 一 个 功 能:ISAPI 可 以 把 请 求 传 递 给ISA 内 特 定 的 成 员 函 数。 请 求 字 符 串 可 以 包 含 一 个 命 令, 解 析 映 射 表 就 使 用 该 命 令 把 请 求 传 递 给ISA 内 正 确 的 成 员 函 数。 - 由 于ISAPI 使 用 命 令 驱 动 机 制 来 处 理 请 求, 因 此 在 开 始 开 发ISA 程 序 时 可 能 会 觉 得 有 些 麻 烦, 但 是 一 旦 学 会 了, 用 户 就 会 发 现 这 是 一 个
10、非 常 强 大 的 处 理 请 求 的 方 法。 - 2 使 用MFC 开 发ISA 程 序 - 2 1 建 立 工 程 - 开 发ISA 的 第 一 步 工 作 是 建 立 一 个 工 程。 和 创 建 其 它Visual C+ (VC+) 工 程 一 样, 创 建ISA 也 有 一 个wizard 来 指 导 用 户 完 成 初 始 的 步 骤。 打 开VC+, 在File 菜 单 中 选 择New, 然 后 在 对 话 框 中 选 择Projects 面 板, 在 下 面 的 列 表 中 选 择ISAPI Extension Wizard 工 程 类 型, 选 择 适 当 的 路 径,
11、并 把 它 命 名 为HelloWeb, 如 图1 所 示。 图New对 话 框 - 接 着 选 择Ok 按 钮, 于 是 出 现 一 个 对 话 框 让 用 户 选 择 预 创 建 的ISAPI 程 序 类 型, 缺 省 情 况 下 是ISA 程 序, 同 时MFC 被 设 置 为 动 态 链 接。 如 果 用 户 开 发 的 服 务 器 上 有 了 这 些MFC DLLs, 这 当 然 是 可 以 的。 但 是 如 果 没 有 安 装Developer Studio, 通 常 情 况 下 这 些DLLs 是 没 有 的, 这 样 用 户 的ISA 将 无 法 运 行。 此 时 应 该 把
12、工 程 设 置 为 静 态 链 接。我 们 建议 用 户 这 样 设 置 , 如 图2 所 示 。 图ISAPI Extension wizard对 话 框 - 接 着 选 择Finish 按 钮。VC+ 将 显 示 一 个 对 话 框 给 用 户 一 些 关 于 新 工 程 的 提 示 信 息。 在 此 对 话 框 中 选 择OK。 - 现 在 工 程 已 经 创 建 好 了, 现 在 该 处 理 一 些 复 杂 点 的 问 题 了。 我 们 在 前 面 提 到 过,ISA 在 运 行 时 是IIS 的 一 部 分, 而IIS 又 作 为NT 的 一 个 服 务 而 运 行。 这 一 事 实
13、 使 用 调 试 过 程 变 得 复 杂 了, 因 为 在IIS 运 行 时,VC+ 的 调 试 器 不 能 够 接 管ISA。 为 了 解 决 这 个 问 题, 微 软 公 司 以 两 种 形 式 发 行 了IIS: 作 为 一 项 服 务, 以 及 作 为 一 个 单 独 的 可 执 行 程 序。 对 于 后 一 种 情 况, 我 们 就 可 以 在 命 令 行 上 来 控 制 服 务 器。 虽 然 这 样 可 以 解 决 上 述 问 题 并 使 得 开 发 过 程 变 得 容 易 一 些, 但 实 现 起 来 显 得 很 繁 琐。 下 面 我 们 来 介 绍 这 个 过 程。 - 当 用
14、 户 处 于debug 调 试 模 式 时,VC+ ( 以 及IIS) 将 在 用 户 的 帐 号 和 权 限 下 运 行。 由 于 通 常IIS 完 成 的 一 些 工 作 是 不 允 许 大 多 数 用 户 有 相 应 的 权 限 的, 因 此 用 户( 或 用 户 的 系 统 管 理 员) 需 要 做 以 下 工 作: - 在 桌 面 上 选 择“ 开 始 程 序 管 理 工 具( 公 用) 域 用 户 管 理 器”, 打 开 域 用 户 管 理 器; - 在“ 规 则” 菜 单 中 选 择“ 用 户 权 限”; - 选 择“ 显 示 高 级 用 户 权 限” 检 查 框; - 在“ 权
15、 限” 下 拉 列 表 中 选 择“ 以 操 作 系 统 方 式 操 作”; - 选 择“ 添 加” 按 钮 得 到“ 添 加 用 户 及 组” 对 话 框, 选 择“ 显 示 用 户” 按 钮, 并 在“ 名 称” 列 表 中 选 择 用 户 使 用 的 帐 号, 然 后 选 择“ 添 加” 按 钮; - 选 择“ 确 定” 按 钮; - 对“ 产 生 安 全 审 核” 权 限 重 复 上 述 步 骤。 - 为 了 使 这 些 设 置 生 效, 用 户 必 须 先 退 出 登 录, 然 后 再 登 录 回 来。 - IIS 中 包 含 了 三 项 服 务:FTP Publishing Ser
16、vice, Gopher Publishing Service 和World Wide Web。 由 于 调 试 器 要 在 命 令 行 上 运 行IIS, 所 以 所 有 这 三 项 服 务 都 必 须 停 止。 这 可 以 通 过“ 控 制 面 板” 中 的“ 服 务” 程 序 或 者 使 用IIS 的“Internet 服 务 管 理 器” 来 实 现。 如 果 需 要 进 行 大 量 的 调 试 工 作, 我 们 建 议 用 户 通 过“ 控 制 面 板” 中 的“ 服 务” 程 序 来 关 闭IIS 服 务 并 禁 止 它 们 自 动 启 动, 这 样 可 以 避 免 用 户 每 次
17、 启 动 计 算 机 时 都 要 进 行 关 闭 服 务 的 操 作。 - 接 下 来 就 必 须 对 工 程 进 行 一 些 配 置 了: - 在Project 菜 单 中 选 择Settings 菜 单 项; - 选 择Debug 面 板, 并 在Category 下 拉 列 表 中 选 择General; - 在Executable for debug session 框 中 输 入 或 者 寻 找IIS 执 行 文 件 的 路 径( 通 常 情 况 下 位 于WINNTsystem32inetsrvinetinfo.exe); - 在Program arguments 框 中 输 入
18、-e w3svc,如 图3 所 示 ; 图Debug 面 板 设 置 - 选 择Link 面 板; - 在Output filename 框 中 输 入 被 编 译 后 的DLL 将 被 放 置 的 路 径 和 文 件 名。 这 个 路 径 必 须 位 于Web 服 务 器 的 根 目 录 下 或 者 某 个 虚 拟 目 标 下, 以 便 客 户 可 以 通 过URL 来 访 问。 例 如, 我 们 的Web 服 务 器 的 根 目 录 是c:InetPubwwwroot, 我 们 把helloweb.dll 放 置 在 该 目 录 下, 这 样 客 户 就 可 以 使 用 下 面 的URL
19、来 访 问 它: - - 如 果 用 户 现 在 还 没 有 退 出 登 录 以 改 变 权 限, 请 现 在 行 动, 然 后 再 登 录 回 来。 - ISAPI Extension Wizard 所 产 生 的 缺 省 代 码 已 经 包 含 了 一 个 可 工 作 的ISA 所 需 要 的 一 切, 虽 然 该ISA 还 没 有 任 何 功 能, 但 我 们 不 妨 先 试 着 进 行 编 译 一 下。 - 按 下F5 键 以 运 行ISA, 如 果 弹 出 对 话 框 问 是 否 创 建 工 程 时 选 择Yes。 几 秒 钟 之 后 调 试 器 就 已 经 工 作 了, 此 时II
20、S 应 该 运 行 在 后 台。 现 在 一 切 就 绪 了, 用 户 可 以 在 自 己 喜 欢 的 浏 览 器 中 输 入 上 面 所 提 到 的URL, 并 在 最 后 面 加 上 一 个 问 号 ?, 如 下 所 示: - - 请 注 意 把 域 名 换 成 正 确 的 值。 - 第 一 次 连 接 到ISA 时 可 能 要 花 几 秒 钟 的 时 间, 此 后 该DLL 被 缓 存 到 了 内 存 中, 连 接 速 度 就 会 显 著 提 高。 当DLL 被 装 入 后, 在 浏 览 器 中 应 该 显 示 如 下 信 息: - This default message was pr
21、oduced by the Internet Server DLL Wizard. Edit your CHelloWebExtension:Default() implementation to change it. - 我 们 的 第 一 个ISA 已 经 工 作 了。 - 2 2 分 析 源 代 码 - 一 个ISA 包 含 两 个 主 要 组 成 部 分: 解 析 映 射 表 和 命 令 处 理 函 数。 - 当 一 个 请 求 来 自EXTENSION_CONTROL_BLOCK( 此 结 构 用 于 在 服 务 器 和ISA 之 间 进 行 通 信) 时, 它 被 传 递 到 命
22、令 解 析 映 射 表。 解 析 映 射 表 由 一 些 宏 组 成, 如HelloWeb 工 程 (HELLOWEB.CPP) 中 的: BEGIN_PARSE_MAP(CHelloWebExtension, CHttpServer) / TODO: insert your ON_PARSE_COMMAND() and / ON_PARSE_COMMAND_PARAMS() here to hook up your commands. / For example: ON_PARSE_COMMAND(Default, CHelloWebExtension, ITS_EMPTY) DEFAULT
23、_PARSE_COMMAND(Default, CHelloWebExtension) END_PARSE_MAP(CHelloWebExtension)- BEGIN_PARSE_MAP 宏 标 志 着 解 析 映 射 表 的 开 始, 它 以ISA 的CHttpServer 类 和 基 类 作 为 参 数;ON_PARSE_COMMAND 宏 则 把 一 个 特 定 的 请 求 或 命 令 映 射 到 一 个 命 令 处 理 函 数 中。 它 的 参 数 包 括 命 令 处 理 函 数 名、 函 数 的 类 以 及 请 求 的 格 式;DEFAULT_PARSE_COMMAND 宏 确 定
24、 了 当 请 求 为 空 或 者 与 解 析 映 射 表 不 匹 配 时 调 用 的 函 数。 它 的 参 数 包 括 函 数 名 和 函 数 的 类。 - 命 令 处 理 函 数 是 解 析 映 射 表 中 调 用 的 主CHttpServer 类 的 成 员 函 数。 下 面 就 是HelloWeb 工 程 中 的Default 命 令 处 理 函 数: void CHelloWebExtension:Default(CHttpServerContext* pCtxt) StartContent(pCtxt); WriteTitle(pCtxt); *pCtxt _T(This defau
25、lt message was produced by the Internet); *pCtxt _T (Server DLL Wizard. Edit your CHelloWebExtension:Default(); *pCtxt _T(implementation to change it.rn); EndContent(pCtxt); - 当 请 求 为 空 或 者 包 含Default 时 此 函 数 被 调 用。 首 先 它 通 过 参 数 得 到 请 求 的CHttpServerContext( 命 令 处 理 函 数 的 第 一 个 参 数 必 须 是 一 个CHttpSer
26、verContext),StartContent 把标 志 放 到pCtxt 中, 然 后WriteTitle 放 置- 2 3 修 改HelloWeb - 下 面 我 们 将 把 缺 省 的 消 息 换 成 我 会 编ISAPI 程 序 了 !。 - 找 到CHelloWebExtension 类 的 成 员 函 数Default, 修 改 成 如 下 形 式: void CHelloWebExtension:Default(CHttpServerContext* pCtxt) StartContent(pCtxt); WriteTitle(pCtxt); *pCtxt _T( 我 会 编I
27、SAPI 程 序 了 !); EndContent(pCtxt); - 然 后 按 照 前 面 方 法 编 译 并 运 行 该DLL, 并 在 浏 览 器 中 重 装 或 者 刷 新URL, 用 户 即 可 看 到 显 示 的 消 息 已 经 变 了。 - 然 而, 如 果 用 户 看 到Server Error 500: Specified module not found. 这 样 的 错 误 信 息, 则 表 示 用 户 的 工 程 是 动 态 链 接 的, 而 所 必 需 的DLLs 不 存 在。 为 了 修 正 这 个 错 误, 需 要 到Project 菜 单 中 选 择Setti
28、ngs 命 令, 然 后 选 择General 面 板, 在Microsoft Foundation Classes 下 拉 列 表 种 选 择Use MFC in a Static Library, 然 后 重 新 编 译 该 工 程。 - 深 入 理 解ISA 编 程 - 进 一 步 分 析 解 析 映 射 表 - 在 解 析 映 射 表 中 使 用 了 五 个 宏: - BEGIN_PARSE_MAP: 开 始 解 析 映 射 表 的 定 义; - ON_PARSE_COMMAND: 解 析 客 户 的 命 令; - ON_PARSE_COMMAND_PARAMS: 把 请 求 的 数
29、据 映 射 到 相 应 的 数 据 结 构 中;- DEFAULT_PARSE_COMMAND: 定 义 缺 省 命 令; - END_PARSE_MAP: 结 束 解 析 映 射 表 的 定 义。 - 我 们 在 来 看 一 看HelloWeb 中 的 解 析 映 射 表: BEGIN_PARSE_MAP(CHelloWebExtension, CHttpServer)/ TODO: insert your ON_PARSE_COMMAND()and / ON_PARSE_COMMAND_PARAMS() here to hook up your commands./ For example:ON_PARSE_COMMAND(Default, CHelloWebExtension, ITS_EMPTY)DEFAULT_PARSE_COMMAND(Default, CHelloWebExtension)END_PARSE_MAP(CHelloWebExtension)- 此 映 射 表 定 义 了 两 个 命 令: 一 个 空 的 请 求 和 缺 省 的Default 命 令。 空 的 请 求 格 式 如 下: - - 它 是 由DEFAULT_PARSE_COMMAND 宏 处 理 的。 - 然 而, 如 果Default 命 令 放 在 了 问 号 的 后 面: -
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1