十四. 名字对象 概念 IMoniker接口 名字对象的创建 根据显示名创建名字对象 类名字对象的创建 其他名字对象的创建

Slides:



Advertisements
Similar presentations
酒店绩效考核攻略 一 业务流程再造 管理环节突破 利润急速倍增 专为您企业量身裁衣服务 突破导师 : 周忠亭副教授 北京大学管理案例研究 中心特聘餐饮讲师 北洋战略研究院研究员 北大时代光华高级讲师 中国十大餐饮管理讲师 中华酒店管理专家教授 教育部首批中国餐饮经理人 师资成员.
Advertisements

实用农业科技写作 王鹏文. 第一章 导论 第一节 农业科技写作概述 一 、 农业科技写作概念和分类: 科技文献类、科技应用类、 科技普及类、科技新闻类 二、 农业科技写作的意义和重要性: 科技工作的重要组成部分、科学研究的手段、 科技成果的反映和标志、科技交流的工具 三、 农业科技写作的特点 : 功利性与及时性、科学性与先进性、读者的专门性与狭隘性、
新课程引领 实践中前行 —— 蓟县初中信息技术三年课改总结. 自从 2005 年秋季我市进入基础教育新一 轮课程改革实验以来,在市教研室的正 确领导下,我县初中信息技术课改工作 稳步推进。三年来,取得了一些成果, 也有不少体会。现将三年来的信息技术 课改工作总结如下。
人力资源工作总结 行政部 人力资源部年度工作 一方面通过招聘管理、劳动合同管理、 入离职管理等,确保各项人事管理工作 的合法性、规范性. 另一方面通过建立员工培训计划,加强 企业文化的贯彻和渗透,提升员工的凝 聚力和归属感,提升员工的敬业度。
河南省基础教育资源网 邓伟鹏 二〇一二年七月 内容大纲 1. 培训平台的目的 2. 培训平台介绍 3. 培训平台功能 4. 培训工作建立流程 5. 培训门户 6. 在线学习 6.1 课程学习 6.2 在线考试 7. 培训考试管理 7.1. 课程管理 7.2 必修学习班建立 7.3 在线考试管理 7.4.
桐乡市地方税务局 2013 年度社会保险费汇算清缴有 关政策及事项说明. 一、政策规定 根据《中华人民共和国社会保险法》、《桐乡市社会保险费征缴管 理办法》(市政府令第 42 号)、《 关于完善社会保险费征缴管理有关问 题的通知》(桐政办发 [2012]152 号)及《关于完善社会保险费征缴管理.
配樂:夢的序曲 ( 鋼琴 ) 雁蕩山因山頂有湖,蘆葦茂密,結草為湯,南歸秋雁多宿於此,故名雁蕩。始於 南北朝,興於唐,盛於宋,雁蕩山來晚了一步,未能在 “ 五岳 ” 中占得一席之地。 沒有金碧輝煌的涂飾,村野之山的雁蕩倒因此多了份瀟灑風神。
1. 法律學系助教群: 大學部助教 徐碧霜 行政助教 葉靜芳 研究所助教 阮博謙 台中 法政學院 1. 台北 法商學院 民國 50 年 中興大學合併法商學院法律系 民國 89 年 法商學院改制為台北大學.
第一节 职业基础知识 第二节 社会需要剖析 第三节 用人单位认知
股指期货的风险及防范.
NO.005 職涯 報 實習 徵才 攻讀 國立嘉義大學 學生事務處學生職涯發展中心.
國中教育會考 十二年國教—免試入學 及 意見整理.
产学研项目财务管理若干问题 鲁春艳
山东大学信息系统平台建设探索 山东大学网络与信息中心 陈琳.
严格标准 规范程序 认真做好党员发展工作.
薪資申報系統操作說明.
商学院 旅游管理专业介绍.
 历史以人类的活动为特定的对象,它思接万载,视通万里,千恣百态,令人销魂,因此它比其他学科更能激发人们的想像力。    
《疯 娘》 --100个人看后99个人会落泪的故事 图文:网络
《数学》(华师大.八年级 下册) 第二十一章数据的整理与初步处理 扇形统计图的制作.
2015届就业指导课程教学大纲介绍.
怎样报销劳务性费用? ——暨薪酬发放申报系统介绍 怎样报销劳务性费用? ——暨薪酬发放申报系统介绍 (学院、部门适用)
Excel高级班 学员 焦攀飞 汪晴讲师 Office套餐 学习心得 自主学习最关键 焦攀飞 赖球 49 D 2056
『臺北市營建剩餘資源管理系統』 教育訓練說明 臺北市政府 報告人 王宏正
自 然 探 索 圓周美語 My name is.
第6章 二叉树和树 前面的章节主要讨论的是线性结构,二叉树和树属于非线性的结构。遍历非线性结构比线性结构要麻烦。
北京吉利大学 教学改革与产教协同 长沙.
“三项制度+一个平台”构建 省级高校教学质量监控体系
年度校樹選拔秀 主辦單位:楊梅國小.
瓯海职专财经专业组简介.
国有资产清查 数据填报操作规范 2016年3月25日.
关注品德与生活课的 探究性学习和微课程的发展
第 19 章 規劃求解求算最大利潤 著作權所有 © 旗標出版股份有限公司.
上海文会会计师事务所有限公司 中国注册会计师 童幸义
2012年度人力资源部工作总结
关于成绩的数理统计的探讨 望您多多指教!多谢!!.
主讲:江西财经职业学院傅文清 联系电话: 教学模式与课程教学设计 主讲:江西财经职业学院傅文清 联系电话:
仓储企业岗位人员招聘 第一组 组员 :陈娇娇 祝婷婷 丁元莉 袁珮 王慧.
22 第 课 增强自我保护的意识和能力.
在课题探索中成长 东风东路小学 王洁华 全国红领巾示范学校 广东省一级学校
关注女职工劳动保护,维护女职工合法权益 ——《女职工劳动保护特别规定》解读
人口与计划生育 统计分析 昌吉市计划生育委员会 二○○六年三月.
执行《劳动合同法》中 应当注意的十大问题.
2014年深圳市学生人身意外伤害保险投保工作介绍 中国人民财产保险股份有限公司深圳市分公司
潘爱民 COM开发 潘爱民
第10章VBA会计应用与实践 第11章会计凭证管理模块设计与实现
5. 瞭解VBA的語法.
潘爱民 COM对象的实现(续) 潘爱民
八. COM跨进程特性 进程外组件 列集 标准列集过程 总体结构 存根 代理 接口列集器 ORPC通道 标准列集的实现 自定义列集
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
五 COM对象 接口及其接口方法的实现 注册表 类厂 COM组件与客户程序的交互过程 类厂的由来 类厂的定义与实现 类厂的创建
COM:moniker、UDT、control
线程同步与异步套接字编程 主讲人:孙鑫
潘爱民 COM:可连接对象 & 结构化存储 潘爱民
潘爱民 自动化(Automation) 潘爱民
第三篇 Excel 2002 第六章 認識Excel 2002 第七章 深入Excel Excel 2002的功能特色
办学条件核查 评估秘书组 电力职业技术学院 山西机电职业技术学院 2014年7月9日.
科 展 說 明.
水利绿色发展问题与建议 姜文来 中国农业科学院农业资源与农业区划研究所.
電腦應用 製作單位: 高雄市立高雄中學.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
怎样报销劳务性费用? ——暨薪酬发放申报系统介绍 怎样报销劳务性费用? ——暨薪酬发放申报系统介绍 (项目经费适用)
成本会计学.
舊生升級編班與新生管理操作說明 全誼資訊股份有限公司 中華民國106年06月05日.
FILE手持 系統 V3.0.
第4章 Excel电子表格 Excel操作必须掌握的知识点: 掌握建立、打开、保存和编辑文档的基本操作 ;
计 算 机 应 用 基 础 潍坊学院 计算机工程学院 主讲人:李凤慧.
新课程理念下如何进行课堂教学 刘志超 2014年2月25日.
第19章 Visual Basic与Excel 联合编程
6 分析資料-以統計測量數呈現.
Presentation transcript:

十四. 名字对象 概念 IMoniker接口 名字对象的创建 根据显示名创建名字对象 类名字对象的创建 其他名字对象的创建 十四. 名字对象 概念 IMoniker接口 名字对象的创建 根据显示名创建名字对象 类名字对象的创建 其他名字对象的创建 简单名字对象的绑定过程。 ROT表 文件名字对象的绑定 CoGetInstanceFromFile 函数 复合名字对象概念及其绑定过程 单项和复合名字对象的创建 单项名字对象的绑定过程 复合名字对象的绑定过程

1 概念 我们已知创建COM对象的两种方法: 通过CoGetClassObject 得到类厂,通过类厂接口调用CreateInstance. 直接使用CoCreateInstance 或CoCreateInstanceEx. 客户使用以上两种方法在创建COM对象时,必须提供对象的CLSID或ProgID。COM提供了提供了第三种方法,即利用名字对象(moniker 绰号,名字)创建COM对象的方法。名字对象本身也是一个COM对象。名字对象为另一个COM对象提供了的符号化的表示方法,同时也对组件对象的创建过程进行封装。客户程序只需创建相应的名字对象,并使用名字对象的绑定功能得到组件对象。 在这个意义上,名字对象类似于类厂对象,但是它比类厂对象提供了更多的功能. 其关系对比图如下:

名字对象与类厂对象的功能对比示意图 IClassFactory (1) 类厂 客户 COM对象 IMoniker (2) moniker

之所以在类厂对象之外衍生出名字对象,是因为: 名字对象可以以名字的方式来创建COM对象,有时候比使用CLSID的方式更方便. 在一些复杂的应用中,COM对象形成了逻辑上的上下级关系或者是包容的关系.在每一个级别上都是一个COM对象,下级的对象只有在上级的对象范围内才有意义,而且在确定了上级对象以后,下级对象可以更加简单(且更加明确和直观)地使用名字的方式来描述. 在这种情形下,使用从上到下的名字比使用一串的CLSID更加方便合理地创建COM对象.比如Excel中的文档对象以Excel Application,WorkBook,WorkSheet、Range等层次的COM对象的方式给应用程序员提供访问接口。这些对象的状态存储在复合文档的不同级别的存储对象和流对象中. 比如“c:\My Documents\MyTable.xls! Sheet2 ! R1C1:R2C2”表示存储在文件My Documents\MyTable.xls 的工作表Sheet2的Range R1C1:R2C2对象。 这些对象往往都是永久对象。它们不仅有方法,而且有状态.它们的状态数据以复合文档的形式存储在磁盘上。如果只使用CLSID,根本不能准确地表明是标识为CLSID的COM对象的此实例,而非彼实例.所以, 不仅是更直观,而且是必须以“复合名字”的方式才能准确地创建或还原这个COM对象.

2. IMoniker接口 IMoniker接口 派生自IPersistStream接口(p242) 接口成员函数功能上分为四组: 名字对象是COM对象的封装和创建者.它实现了IMoniker接口. interface IMoniker : IPersistStream { HRESULT BindToObject ([in] IBindCtx *pbc, [in, unique] IMoniker *pmkToLeft, [in] REFIID riid, [out, iid_is(riid)] void **ppv); ……// 其他成员未列出 } IMoniker接口 派生自IPersistStream接口(p242) 接口成员函数功能上分为四组: 1。名字管理 2。实现绑定 3。管理复合名字对象。4。名字解析。其中最为重要的是绑定操作函数BindToObject。 所有的名字对象都实现了IMoniker接口,名字对象封装了组件对象的所有状态处理过程,客户使用IMoniker接口成员。按照统一的方法处理名字对象。

3 名字对象的创建 3.1 根据显示名创建名字对象 名字对象的名字称为显示名(display name)是一种用户可读的名字.显示名可以是文件路径名,这时返回文件名字对象,也可以是组件对象的CLSID,这时返回类名字对象。也可以是经过扩充的以“!”等间隔开的复合名字,(这时返回复合名字对象)。 IMoniker接口有一个方法GetDisplayName可以返回对象的显示名.然而更重要的是根据显示名来创建名字对象. 1。 WINOLEAPI MkParseDisplayName( LPBC pbc, //绑定环境 LPCOLESTR szUserName, //显示名 ULONG *pchEaten, //绑定过程中解析的字符个数 LPMONIKER FAR *ppmk //结果名字对象的指针 ); MkParseDisplayName根据显示名进行解析的结果生成名字对象,并以IMoniker指针的方式返回给客户。

3.2 类名字对象的创建 名字对象有很多种,也有很多别的方法来创建名字对象。 创建一个类名字对象 3.2 类名字对象的创建 名字对象有很多种,也有很多别的方法来创建名字对象。 创建一个类名字对象 WINOLEAPI CreateClassMoniker( REFCLSID rclsid, //该名字对象所命名的对象的CLSID IMoniker **ppmk //结果名字对象的指针 ); 该类名字对象将指向所命名对象的类厂对象.

下面是一个客户从类名字对象得到组件对象的例子: HRESULT GetMyInterface(IMyInterface* *rgpc){ IClassFactory *pcf=0; //声明目标组件对象的CLSID为一个显示名 const OLECHAR pwsz[]= OLESTR(“clsid:E2F41FB8-BE92-4d00-A3DD-D9C285B959C1”); //为绑定和解析名字对象创建一个新的绑定环境 IBindCtx *pbc=0; ULONG cchEaten; IMoniker *pmk=0; HRESULT hr=CreatBindCtx(0,&pbc); //请求COM将显示名转换为名字对象 hr=MkParseDisplayName(pbc,pwsz,&cchEaten,&pmk); if(SUCCEEDED(hr)){//请求名字对象寻找或创建它指向的对象 hr=pmk->BindToObject(pbc,0,IID_IClassFactory,(void**)&pcf); pcf->CreateInstance(0, IID_IMyInterface, &rgpc); //现在我们有了指向所需对象的指针,因此释放名字对象和绑定环境 pmk->Release(); } pbc->Release(); return hr; }

3.3 其他名字对象的创建: 创建一个文件名字对象。 WINOLEAPI CreateFileMoniker( 3.3 其他名字对象的创建: 创建一个文件名字对象。 WINOLEAPI CreateFileMoniker( LPCOLESTR lpszPathName, //文件路径 LPMONIKER FAR *ppmk //结果名字对象的指针 ); 创建一个单项名字对象。 WINOLEAPI CreateItemMoniker( LPCOLESTR lpszDelim, //分割符 LPCOLESTR lpszItem, //显示名 LPMONIKER FAR *ppmk//结果名字对象的指针 创建一个复合名字对象。 WINOLEAPI CreateGenericComposite( LPMONIKER pmkFirst, //第一个名字对象 LPMONIKER pmkRest, //第二个名字对象 LPMONIKER FAR *ppmkComposite//结果名字对象的指针

4 简单名字对象的绑定过程。 4.1 ROT表 COM对象的激活是由COM服务的SCM(服务控制管理器)来完成的.每台支持COM的机器上都有本地的SCM。它响应客户的加载COM对象的请求。一旦对象被激活,SCM就不再介入到客户和组件对象的方法调用过程。客户以名字对象或底层API的形式访问SCM提供的服务。SCM使用ROT(Running Object Table)来管理正在运行的,已经被注册的名字对象。如果一个名字对象所指的组件对象已经在运行了,在名字对象的绑定过程中,可以直接连接到此对象上,而不必再启动新的对象。 有两种方式可以得到ROT表:

使用GetRunningObjectTable来得到ROT表的IRunningObjectTable指针 WINOLEAPI GetRunningObjectTable(DWORD reserved, //保留 LPRUNNINGOBJECTTABLE *pprot ); //IRunningObjectTable 接口指针 名字对象在绑定过程中要使用到绑定环境。绑定环境是COM实现的系统对象。客户可以使用CreateBindCtx函数来创建一个绑定环境对象。 WINOLEAPI CreateBindCtx(DWORD reserved, //保留 LPBC FAR* ppbc ); //IBindCtx 接口指针 客户可以从绑定环境中使用接口函数GetRunningObjectTable得到ROT,同时可以控制绑定过程中的一些行为。 以下以文件名字对象为例研究名字对象的绑定过程.

4.2 文件名字对象的绑定 比如客户程序通过文件名字对象来访问一个电子表格文档对象。电子表格文件名为:“c:\My Documents\MyTable.xls”. Excel中的文档对象以Excel Application,WorkBook,WorkSheet、Range等层次的COM对象的方式给应用程序员提供访问接口。这几个对象都是永久对象。它们的状态数据以复合文档的形式存储在磁盘上。 在VBA环境下或VisualBasic环境下,我们可以方便地使用VisualBasic语言访问电子表格文档对象。在C++环境下,可以对这些对象进行更精细的控制。 客户程序调用CreateFileMoniker或MkParseDisplayName函数来创建一个文件名字对象。得到文件名字对象的IMoniker指针。然后调用名字对象的IMoniker::BindToObject函数,一旦函数成功返回,就得到了电子表格文档对象。 BindToObject内部调用了CoGetInstanceFromFile,见下:

STDMETHODIMP FileMoniker::BindToObject(IBindCtx. pbc, IMoniker STDMETHODIMP FileMoniker::BindToObject(IBindCtx *pbc,    IMoniker *pmkToLeft,     REFIID riid, void **ppv) { *ppv = 0; HRESULT hr = E_FAIL; if (pmkToLeft == 0) { // 左边没有名字对象的情形 MULTI_QI mqi = { &riid, 0, 0 };   //用户指定的接口IID COSERVERINFO *pcsi;    DWORD grfMode;    DWORD dwClsCtx; // 这三个参数是 BindCtx的属性    this->MyGetFromBindCtx(pbc, &pcsi, &grfMode, &dwClsCtx);   //得到绑定环境,比如使用COM的CreateBindCtx函数,然后可以得到调用IBindCtx的成员函数GetRunningObjectTable得到ROT。   hr = CoGetInstanceFromFile(pcsi, 0, 0, dwClsCtx, grfMode, this->m_pszFileName,   1, &mqi);     if (SUCCEEDED(hr))      *ppv = mqi.pItf; } //通过MULTI_QI 指针的分量指针返回客户对象的接口指针 else { // 左边有名字对象的情形,复合名字对象。见下文. } return hr; } CoGetInstanceFromFile函数完成了实际的创建COM对象的过程. 如下:

4.3 CoGetInstanceFromFile 函数 HRESULT CoGetInstanceFromFile( COSERVERINFO * pServerInfo, //指向远程主机 CLSID* pclsid, //要创建的对象的CLSID IUnknown * punkOuter,//用于被聚合的情形,指向外部的 IUnknown DWORD dwClsCtx, //环境变量 DWORD grfMode, //打开模式 OLECHAR* szName, //对象文件名 ULONG cmq, //MULTI_QI 数组的大小,即接口的个数. MULTI_QI * rgmqResults //MULTI_QI 数组 ); 其中: typedef struct _MULTI_QI { const IID* pIID; //客户指定的要返回的目标对象的接口IID IUnknown * pItf; // 用来保存接口的指针 HRESULT hr; } MULTI_QI; 此结构可以保存多个接口指针。

CoGetInstanceFromFile函数的过程: 1。首先要获得对象的CLSID,客户有可能直接在参数中提供,如果没有提供, CoGetInstanceFromFile调用GetClassFile函数以获得与文件相关的CLSID HRESULT GetClassFile([in, string] OLECHAR *pwszFileName,                    [out] CLSID *pclsid); 如果文件是复合文档,那么该复合文档的根存储对象的IPersist接口的GetClassID成员函数将返回CLSID,如果不是复合文档那么通过注册表查找文件的扩展名与ProgID的Association关系,然后再查找ProgID与CLSID的对应关系取得文件相关的CLSID 2。 得到了对象的CLSID后,由于客户在调用CoGetInstanceFromFile之前已经得到了绑定环境。 CoGetInstanceFromFile可以利用此绑定环境得到ROT(Running Object Table)。ROT表中包含这样的入口项(pmkObjectName, pUnkObject)其中pmkObjectName指向关联目标对象的名字对象的指针, pUnkObject指向运行的对象的指针。那些允许名字对象关联的对象在运行时会在ROT表中注册,即加入入口项。名字对象如果检测到此入口项时,就可以使用ROT表的GetObject函数直接返回目标对象的接口指针(IUnkown指针)。如果没有检测到,那么:

3. 如果在ROT表中没有找到对应的入口项,那么CoGetInstanceFromFile使用CoCreateInstance或CoGetClassObject再由类厂接口的CreateInstance创建新对象,并返回对象的IUnkown接口指针。 4。从IUnkown接口查询对象的IPersistFile接口。(既然复合文档中把它的CLSID记下来了,说明此对象肯定是永久对象,并实现了IPersistFile接口)。从IPersistFile接口调用Load方法,并把文件名传入作为参数,Load从文件读取数据对对象的状态进行初始化,并且在ROT表中进行注册。 Load方法如下:

STDMETHODIMP MyWorkBook::Load(const OLECHAR STDMETHODIMP MyWorkBook::Load(const OLECHAR *pszFileName,  DWORD grfMode) { //从文件中读入对象状态 hr = this->MyReadStateFromFile(pszFile, grfMode); if (FAILED(hr)) return hr; IRunningObjectTable *prot = 0; hr = GetRunningObjectTable(0, &prot); // 从SCM中获取ROT表 if (SUCCEEDED(hr)) { // 创建一个文件名字对象并在ROT中注册     IMoniker *pmk = 0;    hr = CreateFileMoniker(pszFileName, &pmk);   //创建文件名字对象 if (SUCCEEDED(hr)) { // register self in ROT      hr = prot->Register(0, this, pmk, &m_dwReg);    //注册   pmk->Release();     }    prot->Release(); } return hr; } 以后再用同样的文件名调用CoGetInstanceFromFile函数将不会创建新的对象,而是返回指向这个对象的引用。使用文件名字对象达到了两个目的: 允许对象把自身注册到ROT中,使得以后的CoGetInstanceFromFile能找到它。 把CoGetInstanceFromFile隐藏到IMoniker接口后面了。客户只是调用名字对象,并没有调用CoGetInstanceFromFile。见BindToObject的实现代码。

5 复合名字对象概念及其绑定过程 5.1 概念 对于简单名字对象而言,名字对象仅仅是利用字符串间接创建COM对象的一种手段。而且客户通过名字对象引用组件程序的内部对象也非常不方便(比如我们通过以上办法得到了Excel Application对象,但是如果我们要访问其中的一个单元格,以上步骤是不够的。)复合名字对象将真正体现名字对象的优越性。 复合名字对象按从左到右的顺序保存各成员名字对象。比如“c:\My Documents\MyTable.xls! Sheet2 ! R1C1:R2C2”表示一个复合名字对象,它表示存储在文件My Documents\MyTable.xls 的工作表Sheet2的Range R1C1:R2C2对象。 从根本上说,复合名字对象是为了描述现实中存在的一些对象模型,比如Office中的对象模型,这些对象模型中存在着上下级的包容关系。比如Excel 的Application对象是WorkBook对象的上级,WorkBook对象是WorkSheet对象的上级,WorkSheet对象是Range对象的上级。这些都是COM对象,而且都是永久对象,自动化对象。这些对象我们称为单项名字对象. 每个对象只有在它的上级对象(或者称为包容器对象)的环境下才有意义。所以这些对象天生就是“复合”的。而且它们的名字只有在上级对象的命名空间下才有意义。而且,上级对象应该能控制下级对象,比如解析它的名字,创建子对象等等。

复合名字对象由一组名字对象组成,也可以包括其他的复合名字对象。 创建一个复合名字对象。 WINOLEAPI CreateGenericComposite( LPMONIKER pmkFirst, //第一个名字对象 LPMONIKER pmkRest, //第二个名字对象 LPMONIKER FAR *ppmkComposite//结果名字对象的指针 ); 从此函数的结构可知复合名字对象是如何构成的。

5.2 单项和复合名字对象的创建 创建一个单项名字对象。 WINOLEAPI CreateItemMoniker( 5.2 单项和复合名字对象的创建 创建一个单项名字对象。 WINOLEAPI CreateItemMoniker( LPCOLESTR lpszDelim, //分割符 LPCOLESTR lpszItem, //显示名 LPMONIKER FAR *ppmk//结果名字对象的指针 ); 以下代码可以创建一个复合名字对象“File1!Item1!Item2”: CreateFileMoniker(“File1”,&pmkFile); CreateItemMoniker(“!”,”Item1”,&pmkItem1); pmkFile->ComposeWith(pmkItem1,FALSE,&pmkComp1) //或者 CreateGenericComposite(pmkFile,pmkItem1,&pmkComp1) CreateItemMoniker(“!”,”Item2”,&pmkItem2); pmkComp1->ComposeWith(pmkItem2,FALSE,&pmkComp2) //或者 CreateGenericComposite(pmkComp1,pmkItem2,&pmkComp2)

5.3 单项名字对象的绑定过程 单项名字对象所命名的对象的上级对象(或容器对象)必须实现IOleItemContainer接口.以把容器对象和下级对象联系起来, 接口的定义如下: // from oleidl.idl [ object,uuid(0000011c-0000-0000-C000-000000000046) ] interface IOleItemContainer : IOleContainer { // ask for object named by pszItem HRESULT GetObject(         [in] LPOLESTR pszItem, // 对象的显示名     [in] DWORD dwSpeedNeeded, // 时限   [in, unique] IBindCtx *pbc,// 绑定环境     [in] REFIID riid, // 对象的接口       [out, iid_is(riid)] void **ppv); // 返回的接口指针 ……// 其他方法 } 此接口类似于类厂接口, GetObject则类似于类厂接口的CreateInstance函数. 容器对象利用此接口来创建下级对象. 单项名字对象的绑定过程如下:

STDMETHODIMP ItemMoniker::BindToObject(IMoniker. pmkToLeft, IBindCtx STDMETHODIMP ItemMoniker::BindToObject(IMoniker *pmkToLeft, IBindCtx *pbc,   REFIID riid, void **ppv) {*ppv = 0; if (pmkToLeft == 0) return E_INVALIDARG; // 需要一个作用域    // 首先绑定左边的。 IOleItemContainer *poic = 0; HRESULT hr = pmkToLeft->BindToObject(0 ,pbc,   IID_IOleItemContainer, (void**)&poic); //返回左边名字对象所指的对象的IOleItemContainer接口。如果左边也是一个单项名字对象,则会继续递归调用.直到能够单独绑定为止. if (SUCCEEDED(hr)) { pbc->RegisterObjectBound(poic); //在绑定环境中缓存已绑定对象。   DWORD dwBindSpeed = this->MyGetSpeedFromCtx(pbc); //绑定过程比较耗时,所以要设定时间参数。 hr = poic->GetObject(m_pszItem, //即字符串“Sheet2” dwBindSpeed, //实际限制 pbc, //绑定环境 riid, //客户指定的目标对象的接口IID,比如说IID_IWorkSheet ppv);  //返回指针   poic->Release(); } } 注意poic是左边的名字对象所指的对象的IOleItemContainer接口指针。它当然应该知道如何创建自己的下级对象。并返回接口指针。

5.4 复合名字对象的绑定过程 IBindCtx *pbc=0; ULONG cchEaten; IMoniker *pmk=0; 在简单文件名字的对象的绑定过程中演示了“c:\My Documents\MyTable.xls”的绑定过程. 文件“c:\My Documents\MyTable.xls”代表了一个WorkBook对象。而这个WorkBook的第二张工作表Sheet2是它的一个下级对象。Sheet2对象的状态数据存储在复合文档c:\My Documents\MyTable.xls的某一个子存储对象或流对象中。如果我们要在程序中访问这个对象,那么可以使用“c:\My Documents\MyTable.xls!Sheet2”这个名字。以下将演示如何绑定这个复合名字对象. 首先仍然要使用CreateFileMoniker或MkParseDisplayName创建文件名字对象。 OLECHAE pwsz[]=OLESTR(“c:\My Documents\MyTable.xls!Sheet2”) IBindCtx *pbc=0; ULONG cchEaten; IMoniker *pmk=0; HRESULT hr=CreatBindCtx(0,&pbc); hr=MkParseDisplayName(pbc,pwsz,&cchEaten,&pmk); 得到名字对象以后进行绑定工作: IMoniker::BindToObject();

绑定工作分为以下几步: 检查ROT表,如果找到与它相等的名字对象,则调用此名字对象所指对象的IUnknown接口的QueryInterface函数返回接口指针即可.避免启动同一个实例.如果没有找到,则下一步: 把名字对象分成两个部分.pmkLeft和pmkRight.pmkRight是一个简单的名字对象,左边可能是一个复合的或简单的名字对象. pmkRight->BindToObject(pmkLeft……). 在以上函数中,将调用pmkLeft->BindToObject.如果pmkLeft是一个简单名字对象,则按照上节的途径进行绑定工作,如果pmkLeft本身是一个复合名字对象,则重新回到步骤1. 绑定过程形成了递归循环. 复合名字对象的成员个数有限,所以循环一定会终止.以下条件会终止循环: 在ROT表中找到了要绑定的对象. pmkLeft是简单名字对象 pmkRight不需要左边的对象进行绑定支持. 绑定过程完成以后,最左边的对象(最上层的)使用简单名字对象到达方式创建对象(CoCreateInstance见前节). 然后上层容器对象的IOleItemContainer接口 的GetObject函数完成下层对象的创建过程.

前缀 ! 绑定方向 名字对象 后缀 构造方向 绑定和构造的方向

File!Item1!Item2的绑定和构造过程 pmkItem2->BindToObject( 厎 , pmkFileItem1, IID_IUnknown, ppvObj) 客户程序调用 pComp->BindToObject( , IID_IUnknown, &pUnk) 复合名字对象被拆分成 pmkFileItem1 和 pmlItem2 两部分 pmk File Item 1 ->BindToObject( , NULL , IID_I OleItemContainer &p Item1Obj) 被拆分成 pmkFile pmlItem1 pmkItem , pmkFile, IID_ I & p &pFileObj ) 创建文件对象并返回其 IOleItemContainer 接口指针 pFileObj 指向文件对象的 - >GetObject(..., IID_I Item1Obj p 指向文件对象中 Item1 对象的 Item1Obj - IID_IUnknown, &pUnk) pUnk Item2 I Unknown 绑 定 方 向 构 造 File!Item1!Item2的绑定和构造过程