本 文 主 要 介 绍 了 如 何 运 用 一 些Web 服 务 器 所 支 持 的Internet Server API (ISAPI) 编 程 接 口 来 创 建 交 互式 的Web 应 用 程 序(Internet Server Applications, 或 者 简 称 为ISAs), 以及 如 何 调 试ISAPI Extension 程 序。 在 阅 读 本 文 时, 虽 然 不 要求 读 者 对Web/CGI 开 发 有 很 深 的 了 解, 但 是 必 须 具 有 使 用Visual C++ v4.1 以 上 版 本 的MFC 开 发 应 用 程 序 的 经 验。 本 文 中 的 示例 程 序 就 是 在Windows NT 下 用Visual C++ v5.0 创 建 的。
1 . 引 言
—- 1 .1 ISAPI 与CGI
—- 通 用 网 关 接 口Common Gateway Interface (CGI) 很 早 就 作 为 交 互 式 的Web 应 用 程 序 的 一 个 标 准 广 泛 应 用在Internet 之 中。CGI 脚 本 允 许 人 们 用 多 种 编 程 语 言( 如Basic、C、Perl、Shell 等 等) 来 编 写 简 单 的 应 用 程 序。 这 些 脚 本 运 行 在Web 服 务器 上, 而 在 客 户 的Web 浏 览 器 上 输 出 运 行 结 果。 客 户 的输 入 通 过 环 境 变 量 或 者 标 准 输 入 设 备 来 进 行 传 递, 然后CGI 程 序 根 据 需 要 完 成 特 定 的 功 能, 并 通 过 标 准 输 出设 备 送 回HTML 格 式 的 结 果 显 示 在 客 户 的 浏 览 器 中。CGI 的这 一 特 性 — 设 计 简 单, 再 加 上 它 支 持 多 种 编 程 语 言,使 得 开 发CGI 应 用 程 序 非 常 简 单。 尽 管 如 此, 人 们 在 使用 中 还 是 发 现 了CGI 应 用 程 序 的 一 个 很 大 的 缺 点: 性 能不 高。 虽 然 有 不 少 办 法 来 使CGI 应 用 程 序 运 行 得 更 快 一些( 如 把 它 们 变 成 编 译 好 的 二 进 制 代 码, 而 不 用Perl 脚本), 但 执 行 速 度 仍 然 是 一 个 问 题。 每 当 通 过Web 访 问一 个CGI 程 序 时,CGI 执 行 文 件( 或 者 脚 本 的 解 释 器) 都 要为 每 一 个 请 求 创 建 一 个 新 的 进 程。 对 于 一 个 信 息 量 比较 大 的 站 点 来 说, 这 无 疑 给 服 务 器 增 加 了 一 个 沉 重 的负 担。
—- 当 微 软 开 始 开 发 自 己 的Web 服 务器(Microsoft Internet Information Server 或 简 称 为IIS) 时, 意 识 到 了CGI 的 这 一 大 的 缺 陷, 于 是 他 们 就 引 入 了ISAPI。
—- ISAPI 使 用 动 态 链 接 库DLL 而 不 是 可执 行 代 码。 这 些DLLs 被 装 入 到 服 务 器 的 内 存 空 间。 由 于代 码 在 内 存 中 缓 存 起 来 了, 而 不 是 每 次 接 收 到 请 求 时再 装 入 到 内 存 中, 因 此 这 一 技 术 极 大 地 提 高 了 交 互 式的Web 应 用 程 序 的 性 能。
—- ISAPI 程 序 分 为 两 种: 一 种 就 是 我们 要 介 绍 的ISAPI Extensions, 它 提 供 了 一 种 比CGI 更 好 的 实 现方 法; 另 一 种 称 作ISAPI Filter, 它 可 以 对 服 务 器 上 进 入 或出 去 的 数 据 进 行 过 滤。
—- 总 的 来 说,ISAPI 优 于CGI 之 处 包 括:
—- ① 速 度 快:ISAPI 在 性 能 上 有 很 大的 提 高;
—- ② 功 能 强:ISAPI 还 可 以 创 建Filter 以对 数 据 进 行 预 处 理。 并 且 它 完 全 与MFC 集 成 在 一 起 了。
—- 相 反,ISAPI 的 不 足 之 处 有:
—- ① 标 准 化 不 够: 目 前 只 有 一 部分 服 务 器 支 持ISAPI;
—- ② 开 发 困 难: 相 关 资 料 很 少, 并且 调 试 很 麻 烦。
—- 1 .2 ISAPI 基 础
—- ISAs 开 发 主 要 基 于MFC 的CHttpServer 类。该 类 控 制 了 所 有 与 服 务 器 的 交 互 操 作, 同 时 还 包 含 了用 于 客 户 请 求 的 所 有 函 数。 尽 管 一 个ISA 只 能 有CHttpServer 类 的 一 个 实 例, 但 每 个ISA 仍 然 可 以 同 时 处 理 多 个 请求。 这 是 通 过CHttpServer 类 为 每 个 请 求 创 建 一 个CHttpServerContext 类 来 实 现 的。CHttpServerContext 类 包 含 了 每 个 特 定 请 求 的 所有 数 据 以 及 由ISA 返 回 到 客 户 的 所 有HTML 代 码。
—- ISAPI DLLs 的 调 用 方 法 和CGI 一 样: 在客 户 端 使 用GET 或POST 方 法。 例 如, 当 客 户 作 出 下 列 请 求时:
—- http://www.mysite.com/myisa.dll?name=fisherman&id=12345
—- “name” 域 和”id” 域 以 及与 它 们 相 关 的 数 据 都 被 传 递 给ISA。ISAPI 在 使 用 这 些 相 关的 数 据 之 前 把 它 们 存 放 在 相 应 的 数 据 结 构 中, 这 是 通过 一 个 请 求 映 射 系 统 来 实 现 的。
—- 每 一 个 请 求 都 有 一 个 解 析 映 射表。 通 过 定 义 服 务 器 从 客 户 接 收 的 数 据 的 类 型 和 顺序, 该 解 析 映 射 表 可 以 把 数 据 传 递 到 合 适 的 数 据 结 构中。 例 如, 对 于 请 求”name=fisherman&id=12345″, 解 析 映射 表 将 显 示 一 个 字 符 串 和 一 个 整 型 数, 并 且” fisherman ” and “12345″ 将 被 解 析 出 来 存 放 到 各 自 的 数 据 结构 中。
—- 解 析 映 射 系 统 还 有 另 外 一 个 功能:ISAPI 可 以 把 请 求 传 递 给ISA 内 特 定 的 成 员 函 数。 请 求字 符 串 可 以 包 含 一 个 命 令, 解 析 映 射 表 就 使 用 该 命 令把 请 求 传 递 给ISA 内 正 确 的 成 员 函 数。
—- 由 于ISAPI 使 用 命 令 驱 动 机 制 来 处理 请 求, 因 此 在 开 始 开 发ISA 程 序 时 可 能 会 觉 得 有 些 麻烦, 但 是 一 旦 学 会 了, 用 户 就 会 发 现 这 是 一 个 非 常 强大 的 处 理 请 求 的 方 法。
—- 2 . 使 用MFC 开 发ISA 程 序
—- 2 .1 建 立 工 程
—- 开 发ISA 的 第 一 步 工 作 是 建 立 一个 工 程。 和 创 建 其 它Visual C++ (VC++) 工 程 一 样, 创 建ISA 也 有一 个wizard 来 指 导 用 户 完 成 初 始 的 步 骤。 打 开VC++, 在File 菜 单 中 选 择New, 然 后 在 对 话 框 中 选 择Projects 面 板, 在 下面 的 列 表 中 选 择”ISAPI Extension Wizard” 工 程 类 型, 选 择适 当 的 路 径, 并 把 它 命 名 为”HelloWeb”, 如 图1 所 示。图1 New对 话 框
—- 接 着 选 择Ok 按 钮, 于 是 出 现 一 个对 话 框 让 用 户 选 择 预 创 建 的ISAPI 程 序 类 型, 缺 省 情 况下 是ISA 程 序, 同 时MFC 被 设 置 为 动 态 链 接。 如 果 用 户 开发 的 服 务 器 上 有 了 这 些MFC DLLs, 这 当 然 是 可 以 的。 但 是如 果 没 有 安 装Developer Studio, 通 常 情 况 下 这 些DLLs 是 没 有的, 这 样 用 户 的ISA 将 无 法 运 行。 此 时 应 该 把 工 程 设 置为 静 态 链 接。我 们 建议 用 户 这 样 设 置 , 如 图2 所 示 。图2 ISAPI Extension wizard对 话 框
—- 接 着 选 择Finish 按 钮。VC++ 将 显 示 一个 对 话 框 给 用 户 一 些 关 于 新 工 程 的 提 示 信 息。 在 此 对话 框 中 选 择OK。
—- 现 在 工 程 已 经 创 建 好 了, 现 在该 处 理 一 些 复 杂 点 的 问 题 了。 我 们 在 前 面 提 到 过,ISA 在 运 行 时 是IIS 的 一 部 分, 而IIS 又 作 为NT 的 一 个 服 务 而 运行。 这 一 事 实 使 用 调 试 过 程 变 得 复 杂 了, 因 为 在IIS 运行 时,VC++ 的 调 试 器 不 能 够 接 管ISA。 为 了 解 决 这 个 问题, 微 软 公 司 以 两 种 形 式 发 行 了IIS: 作 为 一 项 服 务,以 及 作 为 一 个 单 独 的 可 执 行 程 序。 对 于 后 一 种 情 况,我 们 就 可 以 在 命 令 行 上 来 控 制 服 务 器。 虽 然 这 样 可 以解 决 上 述 问 题 并 使 得 开 发 过 程 变 得 容 易 一 些, 但 实 现起 来 显 得 很 繁 琐。 下 面 我 们 来 介 绍 这 个 过 程。
—- 当 用 户 处 于debug 调 试 模 式 时,VC++ ( 以 及IIS) 将 在 用 户 的 帐 号 和 权 限 下 运 行。 由 于 通 常IIS 完成 的 一 些 工 作 是 不 允 许 大 多 数 用 户 有 相 应 的 权 限 的,因 此 用 户( 或 用 户 的 系 统 管 理 员) 需 要 做 以 下 工 作:
—- ① 在 桌 面 上 选 择“ 开 始\ 程 序\ 管理 工 具( 公 用)\ 域 用 户 管 理 器”, 打 开 域 用 户 管 理 器;
—- ② 在“ 规 则” 菜 单 中 选 择“ 用 户权 限”;
—- ③ 选 择“ 显 示 高 级 用 户 权 限” 检查 框;
—- ④ 在“ 权 限” 下 拉 列 表 中 选 择“ 以 操 作 系 统 方 式 操 作”;
—- ⑤ 选 择“ 添 加” 按 钮 得 到“ 添 加用 户 及 组” 对 话 框, 选 择“ 显 示 用 户” 按 钮, 并 在“ 名称” 列 表 中 选 择 用 户 使 用 的 帐 号, 然 后 选 择“ 添 加” 按 钮;
—- ⑥ 选 择“ 确 定” 按 钮;
—- ⑦ 对“ 产 生 安 全 审 核” 权 限 重 复上 述 步 骤。
—- 为 了 使 这 些 设 置 生 效, 用 户 必须 先 退 出 登 录, 然 后 再 登 录 回 来。
—- IIS 中 包 含 了 三 项 服 务:FTP Publishing Service, Gopher Publishing Service 和World Wide Web。 由 于 调 试 器 要 在 命令 行 上 运 行IIS, 所 以 所 有 这 三 项 服 务 都 必 须 停 止。 这可 以 通 过“ 控 制 面 板” 中 的“ 服 务” 程 序 或 者 使 用IIS 的“Internet 服 务 管 理 器” 来 实 现。 如 果 需 要 进 行 大 量 的调 试 工 作, 我 们 建 议 用 户 通 过“ 控 制 面 板” 中 的“ 服务” 程 序 来 关 闭IIS 服 务 并 禁 止 它 们 自 动 启 动, 这 样 可以 避 免 用 户 每 次 启 动 计 算 机 时 都 要 进 行 关 闭 服 务 的操 作。
—- 接 下 来 就 必 须 对 工 程 进 行 一 些配 置 了:
—- ① 在Project 菜 单 中 选 择Settings 菜 单项;
—- ② 选 择Debug 面 板, 并 在Category 下 拉列 表 中 选 择General;
—- ③ 在Executable for debug session 框 中 输 入或 者 寻 找IIS 执 行 文 件 的 路 径( 通 常 情 况 下 位 于WINNT\system32\inetsrv\inetinfo.exe);
—- ④在Program arguments 框 中 输 入 -e w3svc,如图3 所 示 ; 图3 Debug 面 板 设 置
—- ⑤ 选 择Link 面 板;
—- ⑥ 在Output filename 框 中 输 入 被 编 译后 的DLL 将 被 放 置 的 路 径 和 文 件 名。 这 个 路 径 必 须 位 于Web 服 务 器 的 根 目 录 下 或 者 某 个 虚 拟 目 标 下, 以 便 客 户 可以 通 过URL 来 访 问。 例 如, 我 们 的Web 服 务 器 的 根 目 录 是c:\InetPub\wwwroot\,我 们 把helloweb.dll 放 置 在 该 目 录 下, 这 样 客 户 就 可 以 使 用下 面 的URL 来 访 问 它:
—- http://www.mysite.com/helloweb.dll
—- 如 果 用 户 现 在 还 没 有 退 出 登 录以 改 变 权 限, 请 现 在 行 动, 然 后 再 登 录 回 来。
—- ISAPI Extension Wizard 所 产 生 的 缺 省 代码 已 经 包 含 了 一 个 可 工 作 的ISA 所 需 要 的 一 切, 虽 然 该ISA 还 没 有 任 何 功 能, 但 我 们 不 妨 先 试 着 进 行 编 译 一 下。
—- 按 下F5 键 以 运 行ISA, 如 果 弹 出 对话 框 问 是 否 创 建 工 程 时 选 择Yes。 几 秒 钟 之 后 调 试 器 就已 经 工 作 了, 此 时IIS 应 该 运 行 在 后 台。 现 在 一 切 就 绪了, 用 户 可 以 在 自 己 喜 欢 的 浏 览 器 中 输 入 上 面 所 提 到的URL, 并 在 最 后 面 加 上 一 个 问 号 ?, 如 下 所 示:
—- http://www.mysite.com/helloweb.dll?
—- 请 注 意 把 域 名 换 成 正 确 的 值。
—- 第 一 次 连 接 到ISA 时 可 能 要 花 几秒 钟 的 时 间, 此 后 该DLL 被 缓 存 到 了 内 存 中, 连 接 速 度就 会 显 著 提 高。 当DLL 被 装 入 后, 在 浏 览 器 中 应 该 显 示如 下 信 息:
—- This default message was produced by the Internet Server DLL Wizard. Edit your CHelloWebExtension::Default() implementation to change it.
—- 我 们 的 第 一 个ISA 已 经 工 作 了。
—- 2 .2 分 析 源 代 码
—- 一 个ISA 包 含 两 个 主 要 组 成 部 分:解 析 映 射 表 和 命 令 处 理 函 数。
—- 当 一 个 请 求 来 自EXTENSION_CONTROL_BLOCK(此 结 构 用 于 在 服 务 器 和ISA 之 间 进 行 通 信) 时, 它 被 传递 到 命 令 解 析 映 射 表。 解 析 映 射 表 由 一 些 宏 组 成, 如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_PARSE_COMMAND(Default, CHelloWebExtension)
END_PARSE_MAP(CHelloWebExtension)
—- BEGIN_PARSE_MAP 宏 标 志 着 解 析 映 射 表的 开 始, 它 以ISA 的CHttpServer 类 和 基 类 作 为 参 数;ON_PARSE_COMMAND 宏 则 把 一 个 特 定 的 请 求 或 命 令 映 射 到 一 个 命 令 处 理函 数 中。 它 的 参 数 包 括 命 令 处 理 函 数 名、 函 数 的 类 以及 请 求 的 格 式;DEFAULT_PARSE_COMMAND 宏 确 定 了 当 请 求 为 空 或者 与 解 析 映 射 表 不 匹 配 时 调 用 的 函 数。 它 的 参 数 包 括函 数 名 和 函 数 的 类。
—- 命 令 处 理 函 数 是 解 析 映 射 表 中调 用 的 主CHttpServer 类 的 成 员 函 数。 下 面 就 是HelloWeb 工 程 中的Default 命 令 处 理 函 数:
void CHelloWebExtension::Default(CHttpServerContext* pCtxt)
{
StartContent(pCtxt);
WriteTitle(pCtxt);
*pCtxt << _T(“This default message was produced by the Internet”); *pCtxt < < _T (“Server DLL Wizard. Edit your CHelloWebExtension::Default()”); *pCtxt < < _T(“implementation to change it.\r\n”); EndContent(pCtxt); }
—- 当 请 求 为 空 或 者 包 含Default 时 此函 数 被 调 用。 首 先 它 通 过 参 数 得 到 请 求 的CHttpServerContext(命 令 处 理 函 数 的 第 一 个 参 数 必 须 是 一 个CHttpServerContext),StartContent 把
标 志 放 到pCtxt 中, 然 后WriteTitle 放 置
—- 2 .3 修 改HelloWeb
—- 下 面 我 们 将 把 缺 省 的 消 息 换 成” 我 会 编ISAPI 程 序 了 !”。
—- 找 到CHelloWebExtension 类 的 成 员 函 数Default,修 改 成 如 下 形 式:
void CHelloWebExtension::Default(CHttpServerContext* pCtxt)
{
StartContent(pCtxt);
WriteTitle(pCtxt);
*pCtxt << _T(” 我 会 编ISAPI 程 序 了 !”); EndContent(pCtxt); }
—- 然 后 按 照 前 面 方 法 编 译 并 运 行该DLL, 并 在 浏 览 器 中 重 装 或 者 刷 新URL, 用 户 即 可 看 到显 示 的 消 息 已 经 变 了。
—- 然 而, 如 果 用 户 看 到”Server Error 500: Specified module not found.” 这 样 的 错 误 信 息, 则 表 示 用 户的 工 程 是 动 态 链 接 的, 而 所 必 需 的DLLs 不 存 在。 为 了 修正 这 个 错 误, 需 要 到Project 菜 单 中 选 择Settings 命 令, 然 后选 择General 面 板, 在Microsoft Foundation Classes 下 拉 列 表 种 选 择Use MFC in a Static Library, 然 后 重 新 编 译 该 工 程。
—- 3 . 深 入 理 解ISA 编 程
—- 3 . 1 进 一 步 分 析 解 析 映 射表
—- 在 解 析 映 射 表 中 使 用 了 五 个宏:
—- BEGIN_PARSE_MAP: 开 始 解 析 映 射 表 的定 义;
—- ON_PARSE_COMMAND: 解 析 客 户 的 命 令;
—- ON_PARSE_COMMAND_PARAMS: 把 请 求 的 数 据映 射 到 相 应 的 数 据 结 构 中;
—- 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 命 令。 空 的 请 求 格 式 如 下:
—- http://www.mysite.com/helloweb.dll?
—- 它 是 由DEFAULT_PARSE_COMMAND 宏 处 理 的。
—- 然 而, 如 果Default 命 令 放 在 了 问 号的 后 面:
—- http://www.mysite.com/helloweb.dll?Default
—- 则 此 命 令 由ON_PARSE_COMMAND 宏 处 理。该 宏 的 第 一 个 参 数 定 义 了 命 令 的 名 称, 同 时 它 也 是 处理 该 命 令 的 函 数 的 名 字。 第 二 个 参 数 是 函 数 的 类 名。第 三 个 参 数 用 于 解 析 与 命 令 相 关 的 数 据, 由 于 在HelloWeb 示 例 中 只 传 送 了 命 令 而 没 有 数 据, 因 此 它 被 设 置 为ITS_EMPTY。
—- 如 果 命 令 后 面 还 有 数 据, 则 需要 把 它 解 析 到 合 适 的 数 据 类 型。 例 如, 如 果 作 出 了 下面 的 请 求:
—- http://www.mysite.com/myisapi.dll?Add&name=fisherman&id=12345
—- 则”name” 域 需 要 放 置 到 一 个字 符 串 中,”id” 域 需 要 放 置 到 一 个 整 型 数 中。 为此,ISAPI 定 义 了 6 个 数 据 标 识:
ITS_EMPTY: 没 有 数 据;
ITS_PSTR: 字 符 串;
ITS_I2:short 整 型;
ITS_I4:long 整 型;
ITS_R4:float 浮 点 数;
ITS_R8:double 浮 点 数。
—- 因 此, 在 上 面 的 例 子 中 应 该 有一 个ITS_PSTR 数 据 标 识 和 一 个ITS_I4 数 据 标 识。ON_PARSE_COMMAND 宏将 变 成:
—- ON_PARSE_COMMAND(Add, CMyISAPIExtension, ITS_PSTR ITS_I4)
—- 仅 仅 这 样 还 不 能 正 确 的 工 作,因 为ISAPI 把& 分 隔 符 之 间 的 所 有 字 符 都 解 析 成 一 个域。 因 此,”name=fisherman” 将 被 放 置 到 一 个 字 符 串 中,”id=15248″ 域 被 放 置 到 一 个 整 型 数 中。 这 个 问 题 可 以 通 过 使 用ON_PARSE_COMMAND_PARAMS 宏 来 解 决。 它 应 紧 跟 在ON_PARSE_COMMAND 宏 后 面, 并 创 建 请 求中 的 域 名 的 一 个 映 射 表。 因 此 上 面 的 例 子 应 该 这 样 表示:
—- ON_PARSE_COMMAND(Add, CMyISAPIExtension, ITS_PSTR ITS_I4)
—- ON_PARSE_COMMAND_PARAMS(“name id”)
—- 这 样 就 表 明 了 名 为”name” 的域 应 该 与 解 析 映 射 表 中 的 第 一 个 数 据 类 型 相 联 系,”id” 域 应 该 与 第 二 个 数 据 类 型 相 联 系。
—- 当 创 建 与 解 析 映 射 表 交 互 的HTML 窗 体 时, 要 确 保 窗 体 中 的 动 作 包 含 了 该 命 令, 并 且 窗体 的 方 法 是post。 例 如, 上 面 的 解 析 映 射 表 对 应 的 窗 体应 该 是 这 样 的:
—- 3 . 2 编 写SimpleCalc 示 例
—- 为 了 进 一 步 加 深 认 识, 我 们 再举 一 个 例 子。SimpleCalc 是 一 个 简 单 的 基 于Web 的 计 算 器, 它可 以 进 行 加、 减、 乘、 除 运 算。 缺 省 情 况 下SimpleCalc ISA 会显 示 一 个 窗 体, 其 中 包 含 两 个 用 于 输 入 数 字 的 编 辑 框和 一 个 选 择 运 算 模 式 的 选 择 框。 当 此 窗 体 被 提 交 时,它 将 传 送 一 个”Calc” 命 令, 服 务 器 计 算 答 案, 最 后在 客 户 端 显 示 结 果。
—- 首 先 按 照 前 面 介 绍 的 步 骤 来 创建 一 个 名 为SimpleCalc 的 新 工 程。 接 着 需 要 建 立 解 析 映 射表。 该 表 要 处 理 一 个 缺 省 的 命 令 和 一 个Calc 命 令。 对 于后 者, 编 辑 域num1 和num2 必 须 映 射 到double 浮 点 型 数, 选 择框mode 必 须 映 射 到 字 符 串。 如 下 所 示:
BEGIN_PARSE_MAP(CSimpleCalcExtension, CHttpServer)
//Handle “Calc” command.
ON_PARSE_COMMAND(Calc, CSimpleCalcExtension, ITS_R8 ITS_R8 ITS_PSTR)
//Maps “num1″ and “num2″ to the ITS_R8s, and “mode” to the ITS_PSTR.
ON_PARSE_COMMAND_PARAMS(“num1 num2 mode”)
//Display form if request is empty.
ON_PARSE_COMMAND(Default, CSimpleCalcExtension, ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default, CSimpleCalcExtension)
END_PARSE_MAP(CSimpleCalcExtension)
—- 下 一 步 需 要 改 变default 命 令 处 理函 数 以 便 显 示Calc 窗 体。 代 码 如 下:
void CSimpleCalcExtension::Default(CHttpServerContext* pCtxt)
{
//Print the
tags.
StartContent(pCtxt);
//Print the title.
WriteTitle(pCtxt);
//The next six lines print the default calc form.
//For this form to work correctly the action must contain the “Calc”
//command, and the method must be POST.
*pCtxt << _T(“< H3>SimpleCalc<>
“);
*pCtxt << _T(“< FORM ACTION=”\”simplecalc.dll?Calc\”" METHOD=”POST”>”);
*pCtxt << _T(“< INPUT NAME=”\”num1\”" SIZE=”5″> “);
*pCtxt << _T(“< SELECT NAME=”\”mode\”">< size=1>< ed>+<< _T(“< OPTION>”);
*pCtxt << _T(“< INPUT NAME=”\”num2\”" SIZE=”5″>
“);
*pCtxt << _T(“< INPUT TYPE=”SUBMIT”>”);
//Print
tags.
EndContent(pCtxt);
}
现 在 需 要 编 写Calc 命 令 处 理 函 数。 此 函 数 必 须 决 定 用户 选 择 的 操
作 符 并 相 应 计 算num1 和num2 的 运 算 结 果, 然 后 返 回 该 结果。 其 实 现
代 码 如 下:
void CSimpleCalcExtension::Calc(CHttpServerContext* pCtxt,
double num1, double num2, LPTSTR mode)
{
double result;
//Prints the
tags.
StartContent(pCtxt);
//Prints the title.
WriteTitle(pCtxt);
//Determine the operator.
switch( mode[0] )
{
//Add
case ‘+’ :
result = num1 + num2;
//Print result.
*pCtxt << num1 < < _T(” + “) < < num2 < < _T(“=”) < < result;
break;
//Subtract
case ‘-’ :
result = num1 – num2;
//Print result.
*pCtxt < < num1 < < _T(” “) < < num2 < < _T(“=”) < < result;
break;
//Multiply
case ‘*’ :
result = num1 * num2;
//Print result.
*pCtxt < < num1 < < _T(” * “) < < num2 < < _T(“=”) < < result;
break;
//Divide
case ‘/’ :
result = num1 * num2;
//Print result.
*pCtxt < < num1 < < _T(” / “) < < num2 < < _T(“=”) < < result;
break;
}
//Print < /HTML > < /BODY > tags.
EndContent(pCtxt);
}
</pre>
<p><font color=” #ffffff”>—-
最 后 重 新 编 译 并 运 行 此 工 程。
当 用 户 在 浏 览 器 中 装 入 此ISA 时 将 会 显 示 窗 体。
在 此 窗 体 中 输 入 两 个 数 字, 并 选 择 一 个 操 作 符,
然 后 按 下submit 按 钮。 片 刻 之 后 浏 览 器 中 将 显 示
如 下 的 结 果:
—- 1123.000000 + 23565.000000 = 24688.000000
>> 本文固定链接: http://www.vcgood.com/archives/1390