AcitveX Script

http://dl2.csdn.net/down4/20070918/18185001394.rar

 

2007年7月27日

15:02

  • ActiveX Scripting 技 术

    (一)

    吕 思 伟 潘 爱 民


    ----

    ActiveX Scripting 技 术 是Microsoft ActiveX 技 术 的 一 个 组 成 部 分, 它 的 主 要 目 的 是 使 应 用 程 序 在 不 被 修 改 的 情 况 下, 为 各 种 脚 本 语 言 所 控 制。 在 软 件 交 互 性 不 断 提 高 的 今 天, 仅 仅 提 供 菜 单 或 工 具 箱 的 界 面 已 经 不 能 满 足 用 户 的 需 要 了, 软 件 的 可 定 制 特 性 已 经 成 为 当 今 软 件 的 一 项 基 本 特 征, 尤 其 对 于 一 些 通 用 的 软 件 更 为 如 此。 大 家 比 较 熟 悉 的Microsoft Office, 比 如Word 字 处 理 软 件, 它 不 仅 提 供 了 界 面 的 任 意 定 制, 还 提 供 了 方 便 的BASIC 语 言 的 可 编 程 特 性, 用 户 可 以 通 过 编 写BASIC 语 言 实 现 较 为 复 杂 的 功 能 扩 充。

    ---- Microsoft 提 供 的ActiveX Scripting 技 术 可 使 软 件 扩 充 变 得 非 常 简 单, 软 件 开 发 商 利 用 脚 本 引 擎(Script Engine) 支 持 脚 本 语 言 的 解 释 和 执 行 操 作, 而 软 件 用 户 可 以 根 据 需 要 编 写 自 己 的 脚 本 代 码, 交 由 软 件 处 理。 对 于 用 户 来 说, 就 好 像 自 己 在 编 写 程 序 控 制 应 用 程 序, 以 完 成 自 己 所 期 望 的 功 能。 而 应 用 软 件 并 不 需 要 自 己 去 解 释 执 行 用 户 的 脚 本 代 码, 只 要 利 用 脚 本 引 擎 就 可 以 很 方 便 地 实 现 对 用 户 脚 本 语 言 的 支 持。 应 用 系 统 也 可 以 利 用 这 种 技 术 来 提 供 二 次 开 发 的 特 性。

    ---- 虽 然 脚 本 引 擎 提 供 了 脚 本 语 言 的 解 释 执 行 的 功 能, 但 要 用 好ActiveX Scripting 技 术 则 需 要 对 它 有 一 个 全 面 的 了 解。 本 系 列 讲 座 将 对ActiveX Scripting 技 术 作 一 探 讨, 并 给 出 一 个 具 体 例 子, 以 使 读 者 进 一 步 理 解ActiveX Scripting 技 术。

    一、ActiveX Scripting 背 景

    ---- ActiveX Scripting 是Microsoft 的Automation 技 术 和Script 技 术 结 合 的 产 物。 因 此, 在 介 绍ActiveX Scripting 之 前, 首 先 我 们 来 看 看Automation 和Script 两 种 技 术 的 发 展 情 况。

    ---- 1 .Automation 技 术 是Microsoft OLE 技 术 的 一 部 分, 它 可 使 解 释 性 的 宏 语 言( 主 要 是Visual Basic) 能 够 在 不 了 解 应 用 程 序 的 实 现 细 节 的 情 况 下 控 制Automation 对 象。 随 着Visual Basic 软 件 的 发 展 以 及Microsoft Office 套 件 的 广 泛 应 用,Automation 技 术 已 经 成 为 连 接 这 些 应 用 或 者 工 具 的 纽 带。 而 且, 更 多 的 应 用 把 这 种 宏 语 言 作 为 自 己 应 用 扩 展 的 手 段, 例 如Word、Access 以 及Notes 都 把BASIC 类 语 言 作 为 其 开 发 语 言, 并 且 这 些BASIC 类 语 言 均 支 持Automation 对 象; 另 一 方 面,Internet 浏 览 器 也 提 供 了 脚 本 引 擎, 可 用 于 解 释 网 络 页 面 脚 本 语 言 中 的Automation 对 象。 所 有 这 些 应 用 能 够 得 以 不 断 发 展, 在 很 大 程 度 上 是 因 为 这 些BASIC 语 言 或 脚 本 语 言 提 供 了 对Automation 的 支 持。

    ---- Automation 技 术 以COM( 组 件 对 象 模 型) 为 基 础, 所 有 的Automation 对 象 都 实 现 了 标 准 的IDispatch 接 口, 通 过IDispatch 接 口 暴 露 对 象 的 属 性 和 方 法, 以 便 在 客 户 程 序 中 使 用 这 些 属 性 并 调 用 它 所 支 持 的 方 法。Automation 对 象 的 客 户 程 序 或 者 宿 主 程 序 通 过 类 型 库(Type Library) 获 得 对 象 运 行 时 刻 的 类 型 信 息, 并 提 供 事 件 处 理。 宏 语 言 解 释 器 或 者 脚 本 引 擎 根 据 对 象 的 类 型 信 息, 把 其 中 对 对 象 属 性 和 方 法 的 引 用 解 释 为 对IDispatch 接 口 成 员 函 数Invoke 的 调 用, 从 而 实 现 对 对 象 的 控 制。

    ---- 2 .Script 技 术 是 指 脚 本 语 言 的 技 术, 目 前 主 要 用 于Internet 浏 览 器 中, 它 可 实 现 对 页 面 的 交 互 处 理。 我 们 知 道,HTML 是 一 种 描 述 性 的 语 言, 交 互 能 力 很 弱, 但 通 过Script 技 术, 可 以 编 制 出 一 些 内 容 生 动、 具 有 极 强 交 互 性 的 网 络 页 面; 并 且 使 用Script 技 术 的 另 一 个 好 处 是, 它 能 够 减 轻 服 务 器 端 计 算 的 负 担, 把 部 分 计 算 工 作 转 移 到 客 户 端 来 完 成。 目 前VBScript 和JavaScript 语 言 在 网 络 浏 览 器 上 应 用 非 常 广 泛, 而 且 一 些 主 要 的 浏 览 器 也 提 供 了 相 应 的 引 擎 用 于 处 理 网 页 中 的 脚 本 语 言。

    ---- 通 常 来 说, 用 于 网 络 浏 览 器 的 脚 本 语 言 具 有 以 下 特 点:(1) 它 本 身 是 一 门 解 释 性 语 言, 所 以 语 言 的 语 法 简 单, 但 执 行 效 率 相 对 较 低;(2) 它 采 用 了 事 件 驱 动 机 制, 脚 本 语 言 主 要 用 于 对 交 互 事 件 作 出 响 应, 脚 本 语 言 程 序 的 主 体 是 事 件 处 理 过 程;(3) 与 浏 览 器 内 在 的 对 象 模 型 结 构 结 合 紧 密, 脚 本 语 言 单 独 作 为 编 程 语 言 的 价 值 很 小, 只 有 同 特 定 的 对 象 模 型 结 构 相 结 合 后 才 能 够 体 现 出 其 控 制 能 力。 在HTML 的 脚 本 程 序 中 用 户 可 以 直 接 使 用 如Window、Pane、Document 等 浏 览 器 结 构 元 素, 并 对 其 进 行 控 制, 产 生 各 种 效 果。 此 外 脚 本 语 言 还 可 以 对 页 面 上 的Java Applet 和ActiveX Control 进 行 操 作。 能 够 对 宿 主 应 用 的 对 象 进 行 控 制 正 是 脚 本 语 言 的 真 正 价 值 所 在。

    ---- 从 上 面 对 两 种 技 术 的 讨 论 中 不 难 看 出,Automation 技 术 作 为 对 应 用 程 序 进 行 外 部 控 制 的 成 熟 而 有 力 的 手 段, 其 设 计 目 标 与Script 技 术 有 许 多 共 同 之 处。 而Automation 技 术 的 基 础COM 技 术 本 身 是 一 种 语 言 无 关 的 软 件 模 型, 一 个 很 自 然 的 想 法 是 通 过COM 来 统 一 实 现 对 对 象 的 控 制 和 对 多 种 脚 本 语 言 的 无 缝 支 持。 这 种 想 法 体 现 在 实 践 上 就 形 成 了Microsoft 的ActiveX Scripting 技 术。

    二、ActiveX Scripting 结 构

    ---- 从 技 术 的 角 度 来 看,ActiveX Scripting 技 术 实 际 上 是 一 组COM 接 口 的 定 义, 通 过 这 组 接 口 建 立 应 用 程 序 和 脚 本 引 擎 之 间 的 关 系。 脚 本 引 擎 是ActiveX Scripting 技 术 的 实 现, 一 个 应 用 系 统 如 果 实 现 了 有 关 的 标 准 接 口, 那 么 它 就 可 以 通 过 脚 本 引 擎 提 供 对 用 户 脚 本 语 言 的 支 持。 在 介 绍 这 组 接 口 之 前, 我 们 先 看 看 应 用 系 统、 脚 本 引 擎 和 脚 本 文 件 三 者 之 间 的 关 系。

    ---- 应 用 系 统 为 了 支 持 脚 本 语 言, 首 先 它 要 实 现 几 个 标 准 接 口, 然 后 它 把 自 己 的 一 些 被 控 对 象 暴 露 出 来; 脚 本 文 件 是 一 个 文 本 文 件, 文 件 中 包 含 了 一 些 程 序 代 码; 脚 本 引 擎 本 身 是 一 个COM 对 象, 它 负 责 对 脚 本 文 件 的 解 释 和 执 行 工 作, 在 必 要 时 通 过 应 用 系 统 的 接 口 与 其 交 互。 三 者 的 关 系 如 图1 所 示。

    图1 应 用 系 统、 脚 本 引 擎 和

    脚 本 文 件 三 者 之 间 的 关 系

    ---- 在 图1 中, 应 用 系 统 首 先 需 要 创 建 脚 本 引 擎 对 象, 并 设 置 必 要 的 参 数, 然 后 装 入 脚 本 文 件, 再 启 动 引 擎, 使 引 擎 进 入 连 接 状 态( 即 运 行 脚 本 状 态), 通 常 我 们 通 过 用 户 显 式 操 作( 例 如 通 过 菜 单 命 令 或 快 捷 键) 完 成 启 动 操 作; 应 用 系 统 也 可 以 终 止 引 擎 的 运 行, 使 其 进 入 无 连 接 状 态。 在 引 擎 处 于 连 接 状 态 时, 当 特 定 的 事 件 被 激 发 时, 脚 本 文 件 中 的 相 应 的 事 件 控 制 函 数 会 被 执 行; 在 引 擎 处 于 无 连 接 状 态 时, 即 使 有 事 件 发 生, 脚 本 文 件 中 的 事 件 控 制 函 数 也 不 会 被 执 行。

    ---- 应 用 系 统 也 需 要 实 现 一 些 接 口, 分 别 为:IActiveScriptSite 和IActiveScriptSiteWindow( 可 选)。 接 口IActiveScriptSite 是 每 一 个 支 持 脚 本 语 言 的 应 用 系 统 所 必 须 实 现 的 接 口, 脚 本 引 擎 通 过 它 来 获 取 其 宿 主 程 序 的 信 息, 特 别 是 在 解 释 脚 本 语 言 中 一 些 名 字 时 更 需 要 用 到 这 些 信 息,IActiveScriptSite 的 接 口 定 义 如 下:

    ---- class IActiveScriptSite : public IUnknown

    ---- {

    ---- public :

    ---- virtual HRESULT GetLCID( LCID *plcid) = 0;

    ---- virtual HRESULT GetItemInfo( LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown *ppiunkItem, ITypeInfo *ppti) = 0;

    ---- virtual HRESULT GetDocVersionString(BSTR *pbstrVersion) = 0;

    ---- virtual HRESULT OnScriptTerminate(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) = 0;

    ---- virtual HRESULT OnStateChange( SCRIPTSTATE ssScriptState) = 0;

    ---- virtual HRESULT OnScriptError( IActiveScriptError *pscripterror) = 0;

    ---- virtual HRESULT OnEnterScript( void) = 0;

    ---- virtual HRESULT OnLeaveScript( void) = 0;

    ---- };

    ---- 在 这 些 成 员 函 数 中,GetItemInfo 是 关 键 函 数, 因 为 脚 本 引 擎 管 理 了 一 个 名 字 空 间, 脚 本 引 擎 在 解 释 执 行 过 程 中, 如 果 需 要 某 个 名 字 的 信 息, 则 通 过 宿 主 程 序 的IActiveScriptSite::GetItemInfo 函 数 获 取。 所 以, 应 用 系 统 通 过GetItemInfo 成 员 函 数 把 自 己 的 一 些 对 象 暴 露 给 脚 本 引 擎, 以 便 在 脚 本 文 件 中 引 用。

    ---- IActiveScriptSiteWindow 接 口 是 一 个 可 选 的 接 口, 如 果 在 脚 本 文 件 中 要 用 到 用 户 接 口UI 特 性, 则 应 用 系 统 应 该 实 现IActiveScriptSiteWindow 接 口, 其 定 义 很 简 单, 如 下:

    ---- class IActiveScriptSiteWindow : public IUnknown

    ---- {

    ---- public :

    ---- virtual HRESULT GetWindow( HWND *phwnd ) = 0;

    ---- virtual HRESULT EnableModeless( BOOL fEnable ) = 0;

    ---- };

    ---- 脚 本 引 擎 通 过GetWindow 成 员 函 数 获 取 宿 主 窗 口 句 柄, 作 为 脚 本 中 弹 出 窗 口 的 父 窗 口。

    ---- 除 了 应 用 系 统 需 要 实 现 上 面 两 个 接 口 用 作 它 与 脚 本 引 擎 之 间 的 通 讯 之 外, 脚 本 引 擎 也 实 现 了 一 组 接 口 用 作 两 者 之 间 的 通 讯, 这 组 接 口 包 括:IActiveScript、IActiveScriptParse 和 其 它 一 些 用 于 调 试、 状 态 管 理 或 者 错 误 处 理 的 接 口,IActiveScript 和IActiveScriptParse 是 必 须 实 现 的 接 口, 以 下 是 它 们 的 定 义:

    ---- class IActiveScript : public IUnknown

    ---- {

    ---- public:

    ---- virtual HRESULT SetScriptSite( IActiveScriptSite *pass) = 0;

    ---- virtual HRESULT GetScriptSite( REFIID riid, void **ppvObject) = 0;

    ---- virtual HRESULT SetScriptState( SCRIPTSTATE ss) = 0;

    ---- virtual HRESULT GetScriptState( SCRIPTSTATE *pssState) = 0;

    ---- virtual HRESULT Close( void) = 0;

    ---- virtual HRESULT AddNamedItem( LPCOLESTR pstrName, DWORD dwFlags) = 0;

    ---- virtual HRESULT AddTypeLib( REFGUID rguidTypeLib, DWORD dwMajor, DWORD dwMinor, DWORD dwFlags) = 0;

    ---- virtual HRESULT GetScriptDispatch( LPCOLESTR pstrItemName, IDispatch **ppdisp) = 0;

    ---- virtual HRESULT GetCurrentScriptThreadID( SCRIPTTHREADID *pstidThread) = 0;

    ---- virtual HRESULT GetScriptThreadID( DWORD dwWin32ThreadId,SCRIPTTHREADID *pstidThread) = 0;

    ---- virtual HRESULT GetScriptThreadState( SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState) = 0;

    ---- virtual HRESULT InterruptScriptThread( SCRIPTTHREADID stidThread,const EXCEPINFO *pexcepinfo, DWORD dwFlags) = 0;

    ---- virtual HRESULT Clone( IActiveScript **ppscript) = 0;

    ---- };

    ---- class IActiveScriptParse : public IUnknown

    ---- {

    ---- public:

    ---- virtual HRESULT InitNew( void) = 0;

    ---- virtual HRESULT AddScriptlet( LPCOLESTR pstrDefaultName,LPCOLESTR pstrCode, LPCOLESTR pstrItemName, LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, LPCOLESTR pstrDelimiter, DWORD dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags, BSTR *pbstrName, EXCEPINFO *pexcepinfo) = 0;

    ---- virtual HRESULT ParseScriptText( LPCOLESTR pstrCode, LPCOLESTR pstrItemName,IUnknown *punkContext, LPCOLESTR pstrDelimiter,DWORD dwSourceContextCookie, ULONG ulStartingLineNumber,DWORD dwFlags, VARIANT *pvarResult,EXCEPINFO *pexcepinfo) = 0;

    ---- };

    ---- 应 用 系 统 通 过IActiveScript 接 口 控 制 脚 本 引 擎 的 各 种 行 为, 也 可 以 获 取 引 擎 的 各 种 状 态。 通 常, 应 用 系 统 首 先 调 用IActiveScript::SetScriptSite 成 员 函 数 把 自 身 实 现 的IActiveScriptSite 接 口 传 递 给 引 擎, 以 后 引 擎 就 通 过 该 接 口 与 应 用 系 统 通 讯。 而 应 用 系 统 也 可 以 通 过IActiveScript 的 其 它 成 员 函 数 获 取 或 者 设 置 引 擎 的 运 行 状 态。 接 口IActiveScriptParse 用 于 对 脚 本 代 码 的 操 作, 应 用 系 统 利 用IActiveScriptParse 接 口 装 入 脚 本 代 码。( 未 完 待 续)

     

    源文档 <http://www.pcworld.com.cn/99/9901/0133.asp>

     

 

 

 

 

2007年7月27日

15:03

  • ActiveX Scripting 技 术

    (二)

    吕 思 伟 ----潘 爱 民


    ---- 在 介 绍 了 应 用 系 统 和 脚 本 引 擎 所 实 现 的 一 些 关 键 接 口 之 后, 我 们 再 进 一 步 看 看 应 用 系 统 和 脚 本 引 擎 的 协 作 过 程, 如 图2 所 示。

    ---- 图 中 给 出 了8 个 步 骤, 下 面 逐 一 介 绍。

    图2 应 用 系 统 与 脚 本 引 擎 的 协 作 过 程

    ---- (1) 创 建 必 要 的 受 控 对 象, 这 些 受 控 对 象 是 指 将 要 在 脚 本 文 件 中 引 用 到 的Automation 对 象, 通 常 是 应 用 系 统 的 文 档 对 象, 也 可 以 是 某 些ActiveX 控 制;

    ---- (2) 创 建 引 擎 对 象, 不 同 的 脚 本 语 言 使 用 不 同 的 引 擎 对 象, 通 常 我 们 使 用VBScript 引 擎 或 者JavaScript 引 擎, 创 建 得 到 的 接 口 指 针 是 应 用 系 统 控 制 引 擎 的 惟 一 途 径;

    ---- (3) 装 入 脚 本 文 件, 调 用 引 擎 的IActiveScriptParse 接 口 的ParseScriptText 成 员 函 数 把 脚 本 代 码 装 入 到 引 擎 中, 注 意ParseScriptText 成 员 函 数 只 接 收UNICODE 字 符 串, 如 果 程 序 中 用 到 了ANSI 字 符 串, 则 需 要 进 行 转 换 才 能 装 入 到 引 擎 中;

    ---- (4) 加 入 名 字 项, 凡 是 应 用 系 统 中 要 暴 露 给 脚 本 文 件 的 所 有 对 象 都 需 要 加 入 到 引 擎 的 名 字 空 间 中, 可 以 通 过 调 用IActiveScript 接 口 的AddNamedItem 成 员 函 数 来 完 成;

    ---- (5) 启 动 引 擎, 以 便 运 行 脚 本, 直 接 调 用IActiveScript::SetScriptState 成 员 函 数 使 其 进 入 连 接 状 态( 运 行 状 态) 即 可;

    ---- (6) 引 擎 在 执 行 脚 本 时, 首 先 处 理 其 名 字 空 间 中 的 名 字 项, 调 用 应 用 系 统IActiveScriptSite 接 口 的GetItemInfo 成 员 函 数 获 取 每 一 个 名 字 所 对 应 的 受 控 对 象 的 信 息, 主 要 是COM 接 口; 如 果 在 脚 本 中 有 事 件 控 制 函 数 的 话, 则 还 要 获 取 受 控 对 象 的 类 型 信 息;

    ---- (7) 在 脚 本 执 行 过 程 中, 当 特 定 的 事 件 发 生 时, 引 擎 中 的 事 件 控 制 函 数 就 要 被 调 用;

    ---- (8) 在 脚 本 执 行 过 程 中, 有 可 能 会 调 用 到 受 控 对 象 的 属 性 和 方 法, 则 引 擎 会 通 过 它 所 获 取 的 对 象 接 口 调 用IDispatch::Invoke 成 员 函 数;

    ---- 如 果 应 用 系 统 希 望 终 止 引 擎 的 执 行 过 程, 可 以 调 用IActiveScript::SetScriptState 成 员 函 数 使 其 进 入 非 运 行 状 态 即 可。

    ---- 以 上 的 步 骤 基 本 上 反 映 了 应 用 系 统 和 引 擎 之 间 的 协 作 过 程。 在 实 际 使 用 过 程 中, 可 以 根 据 情 况 的 不 同 灵 活 应 用。

    三、ActiveX Scripting 实 现

    ---- 因 为Microsoft 或 者 其 他 的 软 件 厂 商 已 经 为 我 们 提 供 了 脚 本 引 擎( 如 果 机 器 上 安 装 了Internet Explorer, 则VBScript 和JavaScript 的 引 擎 就 已 经 被 安 装 在 机 器 上 了), 所 以 我 们 只 需 要 在 应 用 系 统 中 直 接 使 用 就 可 以。 为 了 使 应 用 系 统 更 好 地 支 持 这 种 脚 本 特 性, 我 们 对 实 现 脚 本 特 性 过 程 中 的 一 些 要 点 作 一 分 析。

    ---- 首 先 我 们 定 义 一 个 通 用 的 类CScriptHost, 它 实 现 了 两 个 接 口IActiveScriptSite 和IActiveScriptSiteWindow, 类 的 定 义 如 下:

        class CScriptHost : public IActiveScriptSite , public IActiveScriptSiteWindow
        {
        public:
          CScriptHost(LPUNKNOWN lpUnkCtrl, LPCOLESTR
     pNamedItem, HWND hWnd);
          virtual ~CScriptHost();
         
          HRESULT CreateScriptEngine();
          HRESULT ParseFile(const char*
    pszFileName,LPCOLESTR pstrItemName);

        public:
            //IUnknown members
          STDMETHOD(QueryInterface)(REFIID riid,void** ppvObj);
            STDMETHOD_(unsigned long,AddRef)(void);
            STDMETHOD_(unsigned long,Release)();

            //IActiveScriptSite members
            STDMETHOD(GetLCID)(LCID  *plcid) ;
    STDMETHOD(GetItemInfo)(LPCOLESTR pstrName,DWORD
    dwReturnMask,IUnknown  * *ppiunkItem,ITypeInfo  * *ppti) ;
    STDMETHOD(GetDocVersionString)(BSTR  *pbstrVersion) ;
    STDMETHOD(OnScriptTerminate)(const VARIANT
     *pvarResult,const EXCEPINFO  *pexcepinfo) ;
    STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState) ;
    STDMETHOD(OnScriptError)(IActiveScriptError  *pscripterror) ;
    STDMETHOD(OnEnterScript)(void) ;
    STDMETHOD(OnLeaveScript)(void) ;

            //IActiveScriptSiteWindow members
            STDMETHOD(GetWindow)(HWND  *phwnd) ;
            STDMETHOD(EnableModeless)(BOOL fEnable);

        public:
            IActiveScript* m_ps;
            IActiveScriptParse* m_psp;

        private:
            UINT m_cref;
            CLSID m_clsidEngine;
            LPUNKNOWN m_lpUnkCtrl;
            LPOLESTR m_pNamedItem;
            HWND m_Wnd;
    };

    ---- 类CScriptHost 通 过 多 继 承 的 方 法 实 现 了 两 个 接 口, 并 负 责 脚 本 引 擎 的 创 建 和 维 护 工 作。 其 数 据 成 员m_ps 和m_psp 用 于 保 存 引 擎 的IActiveScript 和IActiveScriptParse 接 口 指 针; 数 据 成 员m_clsidEngine 记 录 了 引 擎 的 类ID;m_Wnd 记 录 了 应 用 系 统 提 供 给 引 擎 的 主 窗 口;m_lpUnkCtrl 记 录 了 应 用 系 统 的 惟 一 的 一 个 受 控 对 象,m_pNamedItem 记 录 了 受 控 对 象 的 名 字。 在CScriptHost 类 的 构 造 函 数 中 初 始 设 置m_lpUnkCtrl、m_pNamedItem 和m_Wnd 成 员 变 量。 构 造 函 数 和 析 构 函 数 代 码 如 下:

    CScriptHost::CScriptHost(LPUNKNOWN lpUnkCtrl,
    LPCOLESTR pNamedItem , HWND hWnd)
        : m_ps(NULL),m_psp(NULL),m_cref(0)
        {
            m_lpUnkCtrl = lpUnkCtrl;
            m_pNamedItem = pNamedItem;
            m_Wnd = hWnd;

            // the clsid of VBScript Engine
            static CLSID const clsid = {0xb54f3741, 0x5b07, 0x11cf,
    {0xa4, 0xb0, 0x0, 0xaa, 0x0, 0x4a, 0x55, 0xe8}};

            // Default to VBScript
            m_clsidEngine = clsid;
        }

        CScriptHost::~CScriptHost()
        {
            if(m_psp)
            m_psp- >Release();

            // we must first close the script engine
            if(m_ps)
            {
                m_ps- >Close();
                m_ps- >Release();
            }
        }

    ---- 在 构 造 函 数 中, 我 们 指 定 使 用 缺 省 的VBScript 脚 本 引 擎 对 象, 并 设 置 其 相 应 的CLSID。 在 析 构 函 数 中 我 们 不 能 释 放 所 有 引 擎 的 接 口 指 针, 因 为 这 会 导 致 脚 本 引 擎 对 象 释 放 这 个 指 针 时 出 错。 在 析 构 函 数 中 调 用IActiveScript::Close 关 闭 与 脚 本 引 擎 的 连 接。

    ---- 成 员 函 数CreateScriptEngine 是 由 应 用 系 统 调 用 的 函 数, 代 码 如 下:

       HRESULT CScriptHost::CreateScriptEngine()
        {
            HRESULT hr = S_OK;

            hr = ::CoCreateInstance(m_clsidEngine,NULL,CLSCTX
    _INPROC_SERVER,IID_IActiveScript,(void**)&m_ps);
            if (SUCCEEDED(hr))
            {
              // QI the IActiveScriptParse pointer
    hr = m_ps- >QueryInterface(IID_IActiveScriptParse,
    (void**)&m_psp);
            if (FAILED(hr) )
            {
                m_ps- >Release();
                return hr;
            }

            // set the script site
            hr = m_ps- >SetScriptSite(this);
            if ( FAILED( hr ) )
                return hr;

            m_ps- >SetScriptState(SCRIPTSTATE_INITIALIZED);
            // initiate the script engine
            hr = m_psp- >InitNew();
            if (FAILED(hr))
                return hr;

            hr = m_ps- >AddNamedItem(m_pNamedItem,
    SCRIPTITEM_ISVISIBLE|SCRIPTITEM_ISSOURCE);
          }

          return hr;
    }

    ---- CreateScriptEngine 函 数 首 先 创 建 引 擎 对 象, 然 后 把 引 擎 对 象 的IActiveScript 和IActiveScriptParse 接 口 指 针 分 别 赋 给 数 据 成 员m_ps 和m_psp, 最 后 把m_pNamedItem 名 字 加 入 到 引 擎 的 名 字 空 间 中。

    ---- 成 员 函 数ParseFile 可 以 把 脚 本 文 件 装 入 到 引 擎 中, 代 码 如 下:

    HRESULT CScriptHost::ParseFile(const char * pszFileName,
    LPCOLESTR pstrItemName)
    {
          HRESULT hr = S_OK;

          struct _stat stat;
          size_t cch;
          EXCEPINFO ei;
          FILE *pfile;

          if(_stat(pszFileName,&stat))
              return E_FAIL;
          cch = stat.st_size;

          char* pszAlloc = new char[cch + 1];
          pszAlloc[cch] = '/0';// this is vitally important
          if (pszAlloc==NULL)
              return E_OUTOFMEMORY;
          memset(pszAlloc,0,cch);

          //get the script text into a memory block
          pfile = fopen(pszFileName,"rb");
          if (!pfile)
          {
              hr = E_FAIL;
              return hr;
          }
          fread(pszAlloc,cch,1,pfile);
          fclose(pfile);

          LPOLESTR pwszCode;

    int CharCount = MultiByteToWideChar
    (CP_ACP,0,pszAlloc,-1,NULL,0);
          pwszCode = new WCHAR[CharCount];
       MultiByteToWideChar(CP_ACP,0,pszAlloc,-1,
    pwszCode,CharCount);
          size_t t = wcslen(pwszCode);

    hr = m_psp- >ParseScriptText(pwszCode, pstrItemName,NULL,
    NULL, 0,0,0L,NULL,&ei);

          delete []pwszCode;
          delete []pszAlloc;
          return hr;
    }

    ---- ParseFile 函 数 首 先 确 定 脚 本 文 件 的 长 度, 然 后 打 开 文 件 并 装 入 到 内 存 中, 最 后 把 内 存 中 脚 本 代 码 通 过 引 擎 的IActiveScriptParse 接 口 成 员 函 数ParseScriptText 成 员 函 数 装 入 到 引 擎 中。 需 要 注 意 的 是, 我 们 这 里 调 用MuitiByteToWide 函 数 完 成 了 代 码 从ANSI 到UNICODE 码 的 转 换。( 未 完 待 续)

     

    源文档 <http://www.pcworld.com.cn/99/9902/0233.asp>

     

 

 

 

 

2007年7月27日

15:04

  • ActiveX Scripting 技 术( 三)


    ---- ( 接 上 期) 然 后 我 们 看 看 类CScriptHost 中 接 口IActiveScriptSite 的 成 员 函 数GetItemInfo 的 实 现, 因 为 引 擎 调 用GetItemInfo 函 数 获 取 其 名 字 空 间 中 名 字 项 的 信 息, 所 以 我 们 要 在 此 函 数 中 把 应 用 系 统 的 对 象 暴 露 给 引 擎 和 脚 本, 代 码 如 下:

    STDMETHODIMP CScriptHost::GetItemInfo
    (LPCOLESTR pstrName,DWORD dwReturnMask,IUnknown  **ppiunkItem, ITypeInfo  **ppti)
    {
      HRESULT hr = S_OK;
      // initialize the sent-in pointers
      if(dwReturnMask & SCRIPTINFO_ITYPEINFO)
      {
        if(ppti == NULL)
          return E_INVALIDARG;
        *ppti = NULL;
      }
      if(dwReturnMask & SCRIPTINFO_IUNKNOWN)
      {
        if(ppiunkItem == NULL)
          return E_INVALIDARG;
        *ppiunkItem = NULL;
      }
      if(!_wcsicmp(m_pNamedItem, pstrName))
      {
        if(dwReturnMask & SCRIPTINFO_IUNKNOWN)
        {
          // give out the object's IUnknown pointer
          *ppiunkItem = m_lpUnkCtrl;
          static_cast(*ppiunkItem)- >AddRef();
        }
        if(dwReturnMask & SCRIPTINFO_ITYPEINFO)
        {
          IProvideClassInfo* pClsInfo = NULL;
          hr = m_lpUnkCtrl- >QueryInterface(IID_IProvideClassInfo, (void**)&pClsInfo);

          if(pClsInfo != NULL)
          {
            hr = pClsInfo- >GetClassInfo(ppti);
            pClsInfo- >Release();
          }
        }
      }
      return hr;
    }

    ---- 函 数GetItemInfo 首 先 对 输 出 参 数ppiunkItem 和ppti 进 行 有 效 性 检 查, 然 后 判 断 是 否 输 入 的 名 字 与 应 用 支 持 的 受 控 对 象 的 名 字 一 致, 如 果 一 致 的 话, 则 根 据 参 数dwReturnMask 所 指 示 的 标 志, 把 对 象 的IUnknown 接 口 或 者 对 象 的 类 型 信 息 通 过 输 出 参 数 传 递 给 引 擎, 供 引 擎 解 释 执 行 脚 本 代 码 使 用。

    ---- 我 们 再 看 类CScriptHost 中 接 口IActiveScriptSiteWindow 的 成 员 函 数GetWindow 的 实 现。 函 数 比 较 简 单, 只 是 把 应 用 系 统 的 窗 口 句 柄 通 过 输 出 参 数 传 递 给 引 擎, 代 码 如 下:

    HRESULT CScriptHost::GetWindow(HWND  *phwnd)
    {
      if (m_Wnd != NULL) {
        *phwnd = m_Wnd;
        return S_OK;
      } else
        return E_FAIL;
    }

    ---- 类CScriptHost 的 其 他 成 员 函 数 都 比 较 简 单, 有 的 接 口 成 员 函 数 可 以 不 实 现, 仅 仅 返 回S_OK 或 者E_NOTIMPL 即 可, 其 代 码 不 再 一 一 列 举。

    ---- CScriptHost 提 供 了 应 用 系 统 为 支 持 脚 本 代 码 运 行 所 做 的 基 本 工 作,CScriptHost 为 引 擎 提 供 了 应 用 系 统 的 必 要 信 息。CScriptHost 类 是 一 个 通 用 的 类, 如 果 应 用 系 统 只 有 一 个Automation 对 象 暴 露 给 脚 本 代 码, 则 可 以 用CScriptHost 类 快 速 实 现 对 脚 本 代 码 的 支 持。 如 果 应 用 系 统 有 多 个Automation 对 象 要 暴 露 给 脚 本 代 码, 则 需 要 对 上 面 介 绍 的CScriptHost 类 作 些 修 改, 使 其 支 持 多 个 名 字 项 的 处 理。

    四、ActiveX Scripting 实 例

    在 这 一 节, 我 们 通 过 一 个 实 例 来 说 明 如 何 利 用 上 节 提 供 的CScriptHost 类 为 应 用 程 序 加 上 脚 本 特 性。 例 程 序 很 简 单, 只 是 一 个 基 于 对 话 框 的 应 用, 但 对 话 框 中 有 一 个 日 历 控 制, 这 是Microsoft 提 供 的ActiveX 控 制, 它 本 身 也 是 一 个Automation 对 象, 我 们 将 在 脚 本 代 码 中 对 该 日 历 对 象 进 行 控 制, 并 且 在 脚 本 代 码 中 响 应 日 历 控 制 的 一 些 事 件。 图3 是 例 程 序 的 运 行 界 面 图。

    ----

    图3 例 程 序 运 行 界 面 图

    ---- 创 建 例 程 序 的 过 程 并 不 复 杂, 利 用Microsoft Visual C++ 5.0( 或6.0) 提 供 的AppWizard 和ClassWizard 可 以 很 快 创 建 工 程, 并 添 加 各 项 功 能, 下 面 是 其 操 作 过 程。

    ---- (1) 首 先 我 们 创 建 一 个MFC 工 程, 因 为 例 程 序 比 较 简 单, 所 以 我 们 选 择 了 基 于 对 话 框 的 应 用 类 型。 工 程 名 为Script, 对 话 框 类 名 为CScriptDlg。

    ---- (2) 然 后 我 们 在 对 话 框 资 源 模 板 中 添 加 日 历 控 制, 打 开 对 话 框 模 板, 用 右 键 单 击, 从 菜 单 中 选 择"Insert ActiveX Control" 命 令, 选 择Calendar Control, 然 后 调 整 大 小 合 适 即 可, 并 设 置 控 制 的ID 为IDC_CONTROL1。

    ---- (3) 在 对 话 框 模 板 中 添 加 两 个 按 钮"Load Script" 和"Run Script" 放 在 适 当 的 位 置 上。

    ---- (4) 把 上 一 节 完 成 的 文 件ScriptHost.h 和ScriptHost.cpp 加 入 到 工 程 中。

    ---- (5) 在 类CScriptDlg 中 加 入 数 据 成 员m_pScHost, 其 类 型 为CScriptHost *。

    ---- (6) 用ClassWizard 生 成 按 钮"Load Script" 的 消 息 控 制 函 数, 编 写 代 码 如 下。

    void CScriptDlg::OnLoadscript()
    {
    CFileDialog dlg(TRUE, "*.txt","*.txt",OFN_HIDEREADONLY |
     OFN_OVERWRITEPROMPT,"Text files (*.txt)");

      if(dlg.DoModal()==IDOK)
      {
        CString strPath;
        strPath = dlg.GetPathName();
        if (strPath.IsEmpty())
          return;
        if (m_pScHost != NULL)
          m_pScHost->m_ps->Close();
        CWnd *pCalander = GetDlgItem(IDC_CONTROL1);
        m_pUnknownCtrl = pCalander- >GetControlUnknown();

        m_pScHost = new CScriptHost(m_pUnknownCtrl, L"control", m_hWnd);
        HRESULT hr = m_pScHost->CreateScriptEngine();
        hr = m_pScHost- >ParseFile(strPath,L"control");
        GetDlgItem(IDC_RUNSCRIPT)->EnableWindow(TRUE);
        GetDlgItem(IDC_RUNSCRIPT)- >SetWindowText("Run Script");
        return;
      }
      return;
    }

    ---- 在 消 息 控 制 函 数OnLoadscript 中, 首 先 打 开 标 准 文 件 对 话 框, 待 用 户 选 中 脚 本 文 件 后, 取 到 文 件 名, 放 到 变 量strPath 中, 如 果 原 先 已 经 存 在 引 擎 对 象, 则 先 关 闭 引 擎 对 象。 然 后 通 过CWnd::GetControlUnknown 函 数 取 出 日 历 控 制 的IUnknown 接 口 指 针。 完 成 了 这 些 准 备 工 作 后, 再 构 造 一 个CScriptHost 对 象, 把 控 制 的IUnknown 接 口 指 针、 控 制 名 以 及 对 话 框 的 窗 口 句 柄 传 到CScriptHost 对 象 中, 然 后 调 用 其CreateScriptEngine 成 员 函 数 创 建 脚 本 引 擎 对 象, 创 建 完 成 后, 再 调 用ParseFile 成 员 函 数 装 入 脚 本 文 件。 装 入 脚 本 之 后, 设 置"Run Script" 按 钮 使 其 接 收 运 行 脚 本 的 命 令。 注 意, 在OnLoadscript 函 数 返 回 后, 脚 本 引 擎 已 经 创 建 完 成, 脚 本 文 件 也 已 经 装 入 到 引 擎 中, 但 这 时 脚 本 代 码 并 没 有 被 运 行。

    ---- (7) 用ClassWizard 生 成 按 钮"Run Script" 的 消 息 控 制 函 数, 编 写 代 码 如 下。

    void CScriptDlg::OnRunscript()
    {
      if (m_pScHost != NULL) {
        SCRIPTSTATE ss;
        if (FAILED(m_pScHost- >m_ps- >GetScriptState(&ss)))
          return;
        if (ss == SCRIPTSTATE_CONNECTED) {
    m_pScHost- >m_ps->SetScriptState
    (SCRIPTSTATE_DISCONNECTED);
          GetDlgItem(IDC_RUNSCRIPT)->SetWindowText("Run Script");
        } else {
    m_pScHost- >m_ps- >SetScriptState
    (SCRIPTSTATE_CONNECTED);
          GetDlgItem(IDC_RUNSCRIPT)- >SetWindowText("Stop Script");
        }
      }
    }

    ---- OnRunscript 函 数 比 较 简 单, 它 调 用 引 擎 的IActiveScript 接 口 的GetScriptState 成 员 函 数 获 取 当 前 引 擎 的 状 态, 如 果 当 前 引 擎 中 脚 本 正 在 运 行, 则 调 用SetScriptState 成 员 函 数 使 引 擎 停 止 运 行, 引 擎 进 入 非 运 行 状 态, 并 设 置"Run Script" 按 钮 的 标 题 变 为"Run Script"; 如 果 当 前 引 擎 中 脚 本 不 在 运 行, 则 调 用SetScriptState 成 员 函 数 使 引 擎 进 入 运 行 状 态, 并 设 置"Run Script" 按 钮 的 标 题 变 为"Stop Script"。

    ---- (8) 编 译 并 连 接 例 程 序。 至 此 我 们 已 经 完 成 了 例 程 序 的 创 建 工 作, 接 下 来 我 们 再 写 一 个 脚 本 文 件 用 来 测 试 例 程 序 是 否 能 正 确 运 行 脚 本 文 件。 脚 本 文 件 中 的 代 码 分 两 部 分, 一 部 分 是 全 局 的 执 行 代 码, 当 引 擎 首 次 被 启 动 时, 这 部 分 代 码 就 开 始 运 行; 另 一 部 分 是 事 件 响 应 函 数, 当 日 历 控 制 产 生 事 件 时, 脚 本 代 码 中 的 事 件 响 应 函 数 就 会 被 执 行。 为 了 测 试 例 程 序 的 正 确 性, 我 们 使 用 了 下 面 的 脚 本 代 码。

    ' Golobal code
    MSGBOX("Global!")
    '---------------------------------------------
    Sub control_DblClick()
      control.Nextyear
      MSGBOX("You have double-clicked!")
    End Sub
    '----------------------------------------------

    ---- 上 述 脚 本 代 码 并 不 进 行 实 际 的 操 作, 只 是 弹 出 一 个 消 息 框 表 明 脚 本 代 码 获 得 了 控 制。 因 为 我 们 在 例 程 序 的CScriptDlg::OnLoadscript 函 数 中 指 定 了 应 用 受 控 对 象 名 字 为"control", 所 以 在 脚 本 代 码 中 的control_DblClick 函 数 即 指 响 应"control" 对 象 的"DblClick" 事 件, 在 函 数control_DblClick 中, 调 用 了control 对 象 方 法Nextyear 使 当 前 日 历 后 翻 一 年。 并 弹 出 消 息 框 以 示 脚 本 代 码 被 执 行 了。

    ---- 程 序 执 行 过 程 中, 对 事 件 响 应 后 的 情 况 如 图4 所 示。

    图4 例 程 序 响 应 双 击 事 件 后 的 运 行 结 果

    ---- 如 果 我 们 单 击"Stop Script" 按 钮 停 止 脚 本 的 执 行, 则 再 双 击 日 历 控 制, 脚 本 代 码 中 的 事 件 响 应 函 数 不 会 被 执 行; 如 果 用 户 再 单 击"Run Script" 按 钮, 则 事 件 响 应 函 数 会 再 次 被 执 行。 如 果 用 户 希 望 执 行 其 他 的 脚 本 文 件, 则 可 以 单 击"Load Script" 按 钮, 重 新 装 入 脚 本 文 件。 从 这 里 我 们 可 以 看 出, 应 用 程 序 对 脚 本 引 擎 的 控 制 是 非 常 灵 活 的。 读 者 可 以 试 一 试。

    五、 结 束 语

    Active Scripting 技 术 是 近 几 年 发 展 起 来 的 新 技 术, 它 对 于 软 件 的 性 能 扩 展 有 重 要 的 意 义。 从 本 文 以 上 几 节 的 介 绍 可 以 看 出, 在 应 用 系 统 中 提 供 脚 本 语 言 的 支 持 并 不 困 难, 甚 至 非 常 简 单, 因 此, 这 种 技 术 有 着 广 泛 的 发 展 前 景, 而 且 我 们 也 已 经 看 到 越 来 越 多 的 应 用 系 统 提 供 了 脚 本 语 言 的 支 持。

    ---- 本 文 旨 在 对 脚 本 技 术 作 一 个 基 本 的 介 绍, 希 望 文 中 所 讲 述 的 内 容 能 帮 助 读 者 在 工 作 中 用 好 这 种 技 术。 ◎( 全 文 完) ◎

     

    源文档 <http://www.pcworld.com.cn/99/9903/0333.asp>

     

 

使用 Microsoft Office OneNote 2007 进行创建
可在一个位置放置所有笔记和信息

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值