【vb精品源码栏目提醒】:以下是网学会员为您推荐的vb精品源码-【精品】COM 组件设计与应用(11)—IDispatch 及双接口的调用 - 其它资料,希望本篇文章对您学习有所帮助。
COM 组 件 设 计 与 应 用 ( 十 一 ) IDispatch 及 双 接 口 的 调 用 作者:杨老师下载源代码一、前言 前段时间,由于工作比较忙,没有能及时地写作。
其间收到了很多网友的来信询问和鼓励,在此 一 并 表 示 感 谢 。
咳 ......我 也 需 要 工 作 来 养 家 糊 口 呀 ...... 上 回 书 介 绍 了 两 种 方 法 来 写 自 动 化 IDispatch接 口 的 组 件 程 序 ,一 是 用 MFC 方 式 编 写“ 纯 粹 ”的 IDispatch 接 口 ; 二 是 用 ATL 方 式 编 写 “ 双 接 口 ” 的 组 件 。
二 、 IDispatch 接 口 和 双 接 口 使 用 者 要 想 调 用 普 通 的 COM 组 件 功 能 , 必 须 要 加 载 这 个 组 件 的 类 型 库 Type library文 件tlb比 如 在 VC 中 使 用 import。
然 而 , 在 脚 本 程 序 中 , 由 于 脚 本 是 被 解 释 执 行 的 , 所 以 无 法 使用 加 载 类 型 库 的 方 式 进 行 预 编 译 。
那 么 脚 本 解 释 器 如 何 使 用 COM 组 件 那 ? 这 就 是 自 动 化IDispatch组 件 大 显 身 手 的 地 方 了 。
IDispatch 接 口 需 要 实 现 4 个 函 数 , 用 者 只 通 过 这 4 个 函 数 , 调就能实现调用自动化组件中所有的函数。
这 4 个函数功能如下: 组件中提供几个类型库?当然一般都是一个啦。
HRESULT GetTypeInfoCount 但 如 果 你 在 一 个 组 件 中 实 现 了 多 个 IDispatch 接 口 ,那 out UINT pctinfo 就 不 一 定 啦 ( 注 1) 调用者通过该函数取得他想要的类型库。
HRESULT GetTypeInfo 幸 好 , 在 99 的 情 况 下 , 我 们 都 不 用 关 心 这 两 个 函 数 in UINT iTInfo 的 实 现 , 因 为 MFC/ATL 都 帮 我 们 完 成 了 默 认 的 一 个 实 in LCID lcid 现,如果是自己完成函数代码,甚至可以直接返回 out ITypeInfo ppTInfo E_NOTIMPL 表 示 没 有 实 现 。
( 注 2)HRESULT GetIDsOfNames in REFIID riid insize_iscNames LPOLESTR 根 据 函 数 名 称 取 得 函 数 序 号 , 调 用 Invoke 做 准 备 。
为rgszNames 所 谓 函 数 序 号 , 大 家 去 观 察 双 接 口 IDL 文 件 和 MFC in UINT cNames 的 ODL 文 件 ,每 一 个 函 数 和 属 性 都 会 有 id序 号 .... in LCID lcid 这样的描述。
outsize_iscNames DISPID rgDispIdHRESULT Invoke 根据序号,执行函数。
in DISPID dispIdMember 使 用 MFC/ATL 写 的 组 件 程 序 , 我 们 也 不 必 关 心 这 个 函 in REFIID riid 数 的 实 现 。
如 果 是 自 己 写 代 码 ,则 该 函 数 类 似 如 下 实 现 : in LCID lcid switchdispIdMember in WORD wFlags inout DISPPARAMS pDispParams case 1: ..... break out VARIANT pVarResult case 2: ..... break out EXCEPINFO pExcepInfo .... out UINT puArgErr 其 实 , 就 是 根 据 序 号 进 行 分 支 调 用 啦 。
注 3 从 Invoke 函 数 的 实 现 就 可 以 看 出 , 使 用 IDispatch 接 口 的 程 序 , 其 执 行 效 率 是 比 较 低 的 。
ATL 从 效 率 出 发 ,实 现 了 一 种 叫“ 双 接 口 dual”的 接 口 模 式 。
下 面 我 们 来 看 看 ,到 底 什 么 是 双 接口:图 一 、 双 接 口 dual 结 构 示 意 图 从 上 图 中 可 以 看 出 , 所 谓 双 接 口 , 其 实 是 在 一 个 VTAB 的 虚 函 数 表 中 容 纳 了 三 个 接 口 ( 因 为任 何 接 口 都 是 从 IUnknown 派 生 的 , 所 以 就 不 强 调 IUnknown 了 , 叫 做 双 接 口 ) 。
我 们 如 果 从任 意 一 个 接 口 中 调 用 QueryInterface得 到 另 外 的 接 口 指 针 的 话 , 其 实 , 得 到 的 指 针 地 址 都 是 同一 个 。
双 接 口 有 什 么 好 处 那 ? 答 : 好 呀 , 多 好 呀 , 特 别 好 呀 ...... 使用方式 因为 所以脚本语言使用组件 解 释 器 只 认 识 IDispatch 接 口 可以调用,但执行效率最低编 译 型 语 言 使 用 组 件 它 认 识 IDispatch 接 口 可以调用,执行效率比较低 可 以 直 接 调 用 Ixxx 函 数 , 效 率编 译 型 语 言 使 用 组 件 它 装 载 类 型 库 后 , 就 认 识 了 Ixxx 接 口 最高啦 结论 双接口,既满足脚本语言的使用方便性,又满足编译型语言的使用高效性。
于 是 , 我 们 写 的 所 有 的 COM 组 件 接 口 , 都 用 双 接 口 实 现 吗 ? 错 ! 否 ! NO! 如果不是明确非要支持脚本的调用,则最好不要使用双接口,因为: 如果所有函数都放在一个双接口中,那么层次、结构、分类不清 如 果 使 用 多 个 双 接 口 , 则 会 产 生 其 它 问 题 ( 注 4) 双 接 口 、IDispatch 接 口 只 支 持 自 动 化 的 参 数 类 型 ,使 用 受 到 限 制 ,某 些 情 况 下很不方便喽 还 有 很 多 弊 病 呦 , 不 过 现 在 我 想 不 起 来 喽 ......三、使用方法 如 果 你 的 开 发 环 境 是 vc6.0, 那 么 我 们 使 用 第 九 回 中 的 Simple6 组 件 为 例 , 快 去 下 载 呀 ...... 如 果 你 的 开 发 环 境 是 vc.net 2003, 那 么 用 第 十 回 中 的 Simple8 组 件 为 例 , 快 去 下 载 呀 ...... 嘿 嘿 ,其 实 不 下 载 也 没 有 关 系 ,因 为 你 只 要 下 载 本 回 的 示 例 程 序 ,里 面 已 经 包 含 了 所 需 的 组 件 。
但 使 用 前 不 要 忘 了 去 注 册 呀 : regsvr32.exe simple6.dll 或 regsvr32.exe simple8.dll ( 注 意 别 忘了输入组件的安装目录)。
注册成功后,就可以使用了,使用方法有:示例程序 自动化组件的使用方式 简要说明示例 0 在脚本中调用 在 第 九 回 /第 十 回 中 , 已 经 做 了 介 绍 揭 示 IDispatch 的 调 用 原 理 , 但 傻 子 才 去 这 么 使 用 那 ,示例 1 使 用 API 方 式 调 用 会累死了 使 用 CComDispatchDriver示例 2 比 直 接 使 用 API 方 式 要 简 单 多 啦 , 这 个 不 错 ! 的智能指针包装类 使 用 MFC 装 载 类 型 库 的 包 装 简 单 ! 好 用 ! 常 用 ! 但 它 本 质 上 是 使 用 IDispatch 接 口 ,示例 3 方式 所以执行效率稍差 import 方 式 使 用 组 件 , 咱 们 在 第 七 回 中 讲 过 啦 。
常 用 ! 使 用 import 方 式 加 载 类 型示例 4 对双接口组件,直接调用自定义接口函数,不再经过 库方式 IDispatch, 因 此 执 行 效 率 最 高 啦示例 x
vb、java、c、bcb、delphi....... 反 正 我 不 会 , 自 己 去 请 教 高 人 去 吧 :-示 例 一 、 IDispatch 调 用 原 理 篇void demo ::CoInitialize NULL // COM 初 始 化 CLSID clsid // 通 过 ProgID 得 到 CLSID HRESULT hr ::CLSIDFromProgID LSimple8.DispSimple.1 clsid ASSERT SUCCEEDED hr // 如 果 失 败 , 说 明 没 有 注 册 组 件 IDispatch pDisp NULL // 由 CLSID 启 动 组 件 , 并 得 到 IDispatch 指 针 hr ::CoCreateInstance clsid NULL CLSCTX_ALL IID_IDispatchLPVOID pDisp ASSERT SUCCEEDED hr // 如 果 失 败 , 说 明 没 有 初 始 化 COM LPOLESTR pwFunName LAdd // 准 备 取 得 Add 函 数 的 序 号 DispID DISPID dispID // 取 得 的 序 号 , 准 备 保 存到这里 hr pDisp-GetIDsOfNames // 根 据 函 数 名 , 取 得 序 号 的 函 数 IID_NULL pwFunName // 函 数 名 称 的 数 组 1 // 函 数 名 称 数 组中的元素个数 LOCALE_SYSTEM_DEFAULT // 使 用 系 统 默 认 的 语 言 环 境 dispID // 返 回 值 ASSERT SUCCEEDED hr // 如 果 失 败 , 说 明 组 件 根 本 就 没 有ADD 函 数 VARIANTARG v2 // 调 用 Add12 函 数所需要的参数 v0.vt VT_I4 v0.lVal 2 // 第 二 个 参 数 , 整 数 2 v1.vt VT_I4 v1.lVal 1 // 第 一 个 参 数 , 整 数 1 DISPPARAMS dispParams v NULL 2 0 // 把 参 数 包 装 在 这 个 结 构中 VARIANT vResult // 函 数 返 回 的 计 算 结 果 hr pDisp-Invoke // 调 用 函 数 dispID // 函 数 由 dispID 指 定 IID_NULL LOCALE_SYSTEM_DEFAULT // 使 用 系 统 默 认 的 语 言 环 境 DISPATCH_METHOD // 调 用 的 是 方 法 , 不 是 属 性 dispParams // 参 数 vResult // 返 回 值 NULL // 不 考 虑 异 常 处 理 NULL // 不 考 虑 错 误 处 理 ASSERT SUCCEEDED hr // 如 果 失 败 , 说 明 参 数 传 递 错 误 CString str // 显 示 一 下 结 果 str.Format1 2 d vResult.lVal AfxMessageBox str pDisp-Release // 释 放 接 口 指 针 ::CoUninitialize // 释 放 COM示 例 二 、 CComDispatchDriver 智 能 指 针 包 装 类 的 使 用 方 法void demo // 已 经 进 行 过 了 COM 初 始 化 CLSID clsid // 通 过 ProgID 取 得 组 件 的 CLSID HRESULT hr ::CLSIDFromProgID LSimple8.DispSimple.1 clsid ASSERT SUCCEEDED hr // 如 果 失 败 , 说 明 没 有 注 册 组 件 CComPtr IUnknown spUnk 并 // 由 CLSID 启 动 组 件 , 取 得 IUnknown指针 hr ::CoCreateInstance clsid NULL CLSCTX_ALL IID_IUnknownLPVOID spUnk ASSERT SUCCEEDED hr CComDispatchDriver spDisp spUnk // 构 造 只 能 指 针 CComVariant v11 v22 vResult // 参 数 hr spDisp.Invoke2 // 调 用 2 个 参 数 的 函 数 LAdd // 函 数 名 是 Add v1 // 第 一 个 参 数 , 值 为 整 数 1 v2 // 第 二 个 参 数 , 值 为 整 数 2 vResult // 返 回 值 ASSERT SUCCEEDED hr // 如 果 失 败 , 说 明 或 者 没 有 ADD 函 数 , 或者参数错误 CString str // 显 示 一 下 结 果 str.Format1 2 d vResult.lVal AfxMessageBox str 示 例 程 序 中 使 用 了 Invoke2函 数 , 其 实 你 根 据 不 同 的 函 数 , 还 可 以 使 用 Invoke0、 Invoke1、InvokeN、 PutProperty、 GetProperty......等 等 等 , 的 确 很 方 便 。
示例三、加载类型库,产生包装类来使用 这 个 方 法 使 用 更 简 单 一 些 , 如 果 你 观 察 MFC 帮 你 产 生 的 包 装 类 的 实 现 , 你 就 会 发 现 , 其 实 它调 用 的 是 IDispatch 接 口 函 数 。
使 用 vc6.0 的 朋 友 , 步 骤 如 下 :1、 建 立 一 个 MFC 的 应 用 程 序2、 开 启 ClassWizard, 执 行 Add Class, 选 择 From a type library图二、加载类型库3、 然 后 找 到 你 要 使 用 的 组 件 文 件 simple6.dlltlb 文 件 也 可 以 , 选 择 接 口 后 确 认图三、选择类型库中需要包装的接口4、 在 适 当 的 地 方 输 入 调 用 代 码include simple6.h // 包 装 类 的 头 文 件void demo // 已 经 进 行 过 了 COM 初 始 化 IDispSimple spDisp // 包 装 类 的 对 象 spDisp.CreateDispatch _TSimple6.DispSimple.1 //启 动 组 件 spDisp.xxx... // 调 用 函 数 spDisp.ReleaseDispatch // 释 放 接 口 使 用 vc.net 的 朋 友 , 步 骤 如 下 :1、 建 立 一 个 MFC 的 应 用 程 序2、 执 行 菜 单 “添 加 添 加 类 ”, 选 择 MFC 分 类 中 的 “类 型 库 中 的 MFC 类 ”图 四 、 添 加 类 型 库 中 的 MFC 类3、 选 择 组 件 文 件 simple8.dll或 tlb 文 件 , 并 选 择 需 要 包 装 的 接 口图五、选择文件和接口4、 在 适 当 的 位 置 输 入 调 用 代 码include CDispSimple.h // 包 装 类 的 头 文 件void demo // 已 经 进 行 过 了 COM 初 始 化 CDispSimple spDisp // 包 装 类 的 对 象 spDisp.CreateDispatch _TSimple8.DispSimple.1 // 启 动 组 件 spDisp.xxx... // 调 用 函 数 spDisp.ReleaseDispatch // 释 放 接 口示 例 四 、 使 用 import 方 式 调 用 组 件 import 方 式 在 第 七 回 中 已 经 作 过 介 绍 ,这 里 就 不 多 罗 嗦 了 。
大 家 下 载 本 回 的 示 例 程 序 后 ,自己去看吧。
并且一定要掌握这个方法,因为它的运行效率是最快的呀。
四、小结 留作业啦。
在我们以前所实现的所有组件程序中,只添加了接口方法(函数),而没有添加接口 属 性 ( 变 量 ) , 你 自 己 练 习 一 下 吧 , 很 简 单 的 , 然 后 写 个 程 序 调 用 看 看 。
其 实 对 于 VC 来 说 ,调 用 属 性 和 调 用 方 法 没 有 太 大 的 区 别( vc 把 属 性 包 装 为 GetXXX/PutXXX或 getXXX/putXXX的 函 数 方 式 ),但 在 另 外 一 些 语 言 中( 比 如 脚 本 语 言 )则 更 方 便 ,设 置 属 性 值 是 :对 象 .属 性 变量 或 常 量 , 获 取 属 性 值 是 : 变 量 对 象 .属 性 。
本 回 书 至 此 做 一 了 断 , 更 多 组 件 设 计 和 使 用 的 知 识 , 且 听 下 回 分 解 ......注 1: 多 个 自 动 化 接 口 的 实 现 方 法 , 我 们 以 后 再 说 。
注 2: 将 来 介 绍 ITypeLib::GetTypeInfo 的 时 候 , 大 家 再 回 味 IDispatch::GetTypeInfo吧 。
注 3: 在 后 面 介 绍 “ 事 件 ” 的 时 候 , 我 们 会 自 己 真 正 去 实 现 一 个 IDispatch::Invoke 函 数 。
注 4: 介 绍 多 个 双 接 口 实 现 的 时 候 , 会 谈 到 这 个 问 题 。
上一篇:
数据库精品课程网站
下一篇:
那些令你为之触动的好句子,感慨万分