《基于opc协议的数据通讯.pdf》由会员分享,可在线阅读,更多相关《基于opc协议的数据通讯.pdf(93页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、基于 OPC 协议的数据通信 摘 要 OPC 是一种通用的工业标准,OPC(OLE for Proecss Control)是微软公司的对象链接和嵌入技术在过程控制领域的应用,为工业自动化软件面向对象的开发提供一项统一的标准。它是解决应用软件与各种设备驱动程序之间的通信而提出的,它把硬件厂商和应用软件开发商分离开来,大大提高了双方的工作效率。OPC 减少了系统集成过程中的重复工作,代表了今后过程控制系统通信标准的发展方向。论文还分析介绍了 OPC 核心技术 COM(Component Object Modole)组件对象模型,并学习了 COM 组件的实现、发布、创建方法和 COM 技术在工业自
2、动化软件及 OPC 技术中的应用细节。使用连接点技术、Tear-off 技术完成 OPC 接口程序编制。按照 OPC 规范,开发出一个具有与第三方 OPC 客户进行基本数据通信能力的 OPC 数据访问服务器。该服务器实现了同步、异步、订阅等数据采集和同步、异步方式的数据设置功能。关键词 OPC,COM,接口,连接点技术,Tear-off 技术 BASE ON THE SPECEFICATION OF OPC DATA ACCESS ABSTRACT OPC is such a standard that we need.OPC means OLE(Object Linking and Embe
3、dding)for Progress Control.OPC was put forward in solving communication between application software and vary drivers of devices.It makes the work more efficient.Consequently,the repeated exploitations are reduced in the integrated system.OPC specification will be a developmental aspect about proces
4、s automations communication normal for future.In the master student project the Component Object Model as OPC nut technology and its applications detail about the realize、issuance and establish method of COM component.The uses of connection pointer technology and Tear-off technology,the program for
5、OPC interface was accomplished.According to the OPC Data Access Specification,the OPC of Data Access with the basic function has been developed,which can communicate with OPC Client of the third party.This server can complete sync read,async read or async subscription,and sync write or async write.K
6、EY WORDS:opc,com,interface,connectionpoint technology,Tear-off technology 第 i 页 目 录 第一章 绪论1 1.1 OPC 概论1 1.1.1 控制系统现状1 1.1.2 OPC 规范简介2 1.1.3 OPC 技术的发展3 1.1.4 OPC 技术的发展现状3 1.2 课题的主要研究内容及意义4 1.2.1 问题的提出4 1.2.2 主要研究内容5 1.2.3 研究的意义5 1.2.4 论文的主要结构6 第二章 COM 概论7 2.1 COM 与 OPC 的关系7 2.2 COM 技术7 2.3 创建 COM 组件9
7、 2.3.1 建立信道10 2.3.2 线程与进程11 2.3.3 实现 QueryInterface()15 2.3.4 引用计数16 2.4 COM 的数据类型17 2.4.1 BSTR 数据类型17 2.4.2 变量数据类型18 2.5 内存管理 19 第三章 OPC 体系结构22 3.1 OPC 基本框架22 3.2 OPC 规范术语23 第 ii 页 3.2.1 公共组23 3.2.2 数据源与时间戳23 3.2.3 活动状态24 3.2.4 更新率24 3.2.5 OPCHANDLE24 3.3 OPC 中的项结构25 3.4 质 量26 第四章 OPC 通信机制27 4.1 OP
8、C 对象与接口29 4.1.1 OPC 服务器对象29 4.1.2 OPC 组对象30 4.2 数据访问方法33 4.2.1 同步数据访问33 4.2.2 异步数据访问33 4.2.3 订约数据采集方式34 4.3 连接点35 4.4 活动状态和数据源对数据采集的影响37 4.4.1 IOPCSyncIO37 4.4.2 IOPCAsyncIO38 4.4.3 IOPCAsyncIO238 4.4.4 通过 IDataObject 订阅39 4.4.5 通过 IOPCDataCallback 订阅39 第五章 OPC 服务器的开发42 5.1 设计要求42 5.2 平台与工具42 5.3 设计
9、方案42 5.4 系统访问流程图43 5.5 OPC 服务器的总体设计45 5.5.1 OPCServer 类45 第 iii 页 5.5.2 ISimuOPCServer 类46 5.5.3 OPCGroup 类46 5.5.4 OPCItem 类47 5.5.5 ISimuOPCItemMgt 类48 5.5.6 ISimuOPCSyncIO 类48 5.5.7 ISimuOPCAsyncIO2 类48 5.5.8 ISimuOPCGroupStateMgt 类48 5.6 服务器接口类的实现48 5.6.1 添加组48 5.6.2 添加项52 5.6.3 同步读写数据操作53 5.6.4
10、 连接点的实现55 5.6.5 异步数据通信58 5.7 服务器安装部署60 5.7.1 注册 Proxy/Stub DLL60 5.7.2 组件种类注册60 5.7.3 组件的注册项目61 5.8 OPC 服务器的测试62 总结64 参考文献66 附录69 致谢87 攻读学位期间发表的学术论文87 北京化工大学硕士研究生论文 第 1 页 第一章 绪 论 11 OPC 概论 111 控制系统现状 目前,工业自动化系统已经得到广泛应用,系统的集成度越来越高。工业现场可以采用的仪器仪表种类十分丰富,工业控制过程越来越复杂,同时控制系统也逐渐与商业系统结合到一起。随着监视和控制生产过程的 DCS 和
11、 SCADA 系统的使用,以往由手工采集的数据变成自动采集。将现场的数据进一步分析与处理,为与之相关的各种经营销售提供数据也成为可能。传统的实时监控系统由于不具备开放性,各个部分的联系过于紧密,使系统过于复杂,这样系统的更新、扩展、升级都变得非常困难,同时也增加了系统维护的难度。传统的实时监控系统开发中出现的另一个主要问题是软件的重复开发,软件不能够重用,资源不能共享,造成大量人力物力资源的浪费。随着计算机软件的发展,这种情况有所改观,高级语言中库函数的采用,实现了一定程度上资源的共享,尤其是面相对象的方法的应用,使得我们可以利用面相对象的继承等方法大量重用源代码。但这些重用只是对源代码级的重
12、用而不是对可执行文件级的重用,对每一类库都要重新编译,所以并没有真正实现资源共享,并且对某个模块中某个类库的修改将引起所有引用该类库的模块的修改,因此非常难以实现某个模块的升级。同时,为一种语言开发的类库以及函数库都不能够为其他语言所用,也大大限制了软件的重用。一般实时监控系统为分布式的结构,实现了人机接口、通信、数据处理等功能在网络上的分布,同时将一个系统划分为各个子系统,虽然降低了系统的复杂程度,改善了系统性能,减少了开发周期与维护费用,但由于系统各个计算机的通信协议依赖于某个厂家,没有形成统一的标准,不同厂家之间的软件与硬件的集成难于实现,也没有真正实现不同厂家的软件共享。因此这就迫切需
13、要一种能有效进行数据访问和管理的开放标准,使工业控制 第 2 页 计算环境中的各个数据源之间能够灵活地进行通信。OPC(OLE for Process Control,面向过程控制的 OLE)作为硬件与软件之间的一个中间件解决了以上的问题,它为工业计算环境提供了这样一种标准,支持分布式应用和异构环境下软件的无缝集成。供应商可以开发一个高度优化的、可重用的 OPC Server 访问底层的硬件,并将数据以 OPC 接口方式提供给任何支持 OPC规范的客户端软件,客户就可以按照统一的数据访问标准访问不同厂商的硬件产品。112 OPC 规范简介 OPC 规范作为一个工业标准,是开发 OPC 服务器与
14、 OPC 客户软件之间数据传输的规范,并已形成一个体系。根据开发软件功能的不同,OPC 制定了以下领域单独规范:1 数据访问规范:定义了 OPC 服务器中一组 COM 对象及其接口,并规定了客户对服务器程序进行数据存取时需要遵循的标准。借助 Microsoft 的 DCOM 技术,OPC 实现了高性能的远程数据访问能力。23 报警和事件处理规范:该规范提供了一种通知机制,在指定事件或报警条件发生时,OPC 服务器能够主动通知客户程序。4 历史数据访问规范:该规范提供一种通用历史数据引擎,可以向感兴趣的用户和客户程序提供数据汇总和数据分析等额外的信息。56 图 11 基于 OPC 协议的体系 第
15、 3 页 安全性规范:该规范提供了一种专门的机制来保护 OPC 服务器中的现场数据,防止未授权的操作误修改这些参数。7 批量过程规范:该规范基于 OPC 数据存取规范和 ISA88 系列批量控制标准,提供了一种存取实时批量数据和设备信息的方法。8 113 OPC 技术的发展 OPC 基金会(OPC Foundation)是管理 OPC 标准的国际组织,其成员几乎包括了世界上所有的控制系统、仪器仪表和过程控制系统的主要供应商。OPC 规范就是这些领先的自动化软硬件供应商与微软公司合作,制定的一套标准的OLE/COM/DCOM 接口协议。9 第一阶段在1996年8月由OPC Task Force(
16、OPC Foundation的前身)发布了OPC 1.0 版本。随着 1997 年 2 月 Microsoft 公司推出 Windows95 支持的 DCOM 技术,1997 年9 月新成立的 OPC Foundation 对 OPC 规范进行了修改,发布了 OPC 规范 1.0A 版,改名为数据存取规范 1.0A 版(Data Access Standard)。增加了数据访问等一些标准,OPC 规范得到了进一步地完善。1998 年推出 OPC 2.0 版本,修正了 1.0 版本的一些错误,并新增了几个接口,调整了数据通信的方法,使程序更稳定和健壮。1998 年 12 月发布了报警事件规范 1
17、.0 版。1999 年 12 月升级到 1.01 版。1998年还发布了历史数据存取规范 1.0 版。2000 年 1 月发布了批量过程规范 1.0 版。同年 10 月发布了安全性规范 1.0 版。现在网络技术迅猛发展,作为其新技术之一的可扩展标记语言(extensible markup language,XML)非常适合不同应用程序间的数据交换,OPC 基金会专门成立了 XML 工作组,正在努力制定将 OPC 数据向 Internet 上的应用程序开放,从而增强与 OPC 兼容应用程序的电子商务功能的规范。114 OPC 技术的发展现状 OPC 技术在国外已经具有多年的发展历史,已经在过程控
18、制和自动化领域中 第 4 页 进行了大量的应用。而最近,随着 CE 等嵌入式系统的兴起,一些大型的跨国公司正在开发可供嵌入式系统使用的 OPC。.NET 技术加入到 OPC 规范中去,使其适应 XML 技术,OPC 又有了进一步的发展应用。当前 OPC 在国外已不仅仅局限在过程控制领域的应用,已经有使用 OPC 规范成功搭建微波通讯网络的报导,这使OPC 的应用跨入了更广阔的空间。当前国内对 OPC 规范在过程控制领域也做了大量的推广和研究,在电力、化工、水处理、造纸、智能建筑以及国防领域都有广泛的应用,如电厂电力设备监控10、化工流程控制、造纸工业制浆过程 DCS 控制11、智能建筑弱电系统
19、集成12。由于其巨大的技术优势和经济优势在国民经济和国防建设中发挥了良好作用。现在,国内已经有一些公司加入到 OPC 基金会,成为其会员,并开发出一些支持 OPC技术的工控软件,如北京亚控公司的“组泰王”,投放市场后获得了用户的良好反应。12 课题的主要研究内容及意义 121 问题的提出 在生产现场,存在着大量控制器和现场数字设备,这些设备来自不同的制造商,遵从不同的通信标准,只能组成各自的控制系统,与特定的应用软件通信。虽然某些网络之间可通过协议转换实现互联,但并不具有普遍性。传统的过程控制系统中,为使每个应用程序与设备交换信息,必须为每个设备提供相应的驱动程序,由于设备多样性和驱动程序不兼
20、容性,使应用程序开发者必须花大量的精力开发各设备的驱动接口,从而使开发时间和费用大大增加。设备不具有互操作性,使用中硬件的升级、变更和增加都可能引起驱动程序的变化,从而在维护过程中引起二次投资。由于每一个应用程序有各自的驱动程序,当多个应用程序读取同一数据源,经常发生冲突。设备厂商虽然可能提供驱动程序,但与用户开发的应用软件往往协议不一致,限制了客户对软件和硬件的选择。为解决这个一直困扰工业自动化控制领域的难题,国内外都进行了大量的研究,提出了许多的解决方案。比较成功的是 OPC 基金会制定的 OPC 规范,根据该 第 5 页 规范开发的 OPC 服务器,作为设备与应用软件之间通信的中间件,很
21、好地解决了上述问题,OPC 正在逐渐成为过程控制的数据通信标准。我国在这方面的研究起步较晚,自 OPC 规范发布以来,才取得了较大的进展,现已列入国家“八五”科技攻关项目。与此同时,全国现在已有多家科研院校和控制系统公司对其研究,开发自主知识产权的控制软件。122 主要研究内容 针对传统控制过程中的不足和局限性,研究 OPC 技术的体系结构和通信机制,以及 OPC 服务器开发的技术细节。并对 OPC 的技术基础 COM 进行了深入研究,掌握一般基于 COM 的组建开发流程和实现技术。在服务器开发中,提出了 OPC Server 对象、OPC Group 对象和 OPC Item 对象的结构模型
22、,根据各对象实现接口函数,拆分出多个子功能模块,实现同步读写、异步读写、订阅采集等通信方式。兼容 OPC 2.0 的异步通信操作时,使用连接点技术,对其也进行了分析研究。为满足不同的应用程序从各种现场设备获取数据,课题主要涉及如下内容:COM 技术了理论基础和开发细节 OPC 规范的体系结构和通信方式,包括连接点等 OPC 服务器开发的关键技术 OPC 服务器的设计与实现 123 研究的意义 当前我国还有大量早期的 DCS 控制系统,它们使用设备供应商的驱动进行通信,其数据格式并不规范,与现在主流应用程序不能很好地兼容。为了有效地发挥这些原有设备的潜能,使其能连接到当前多客户的应用环境中,对
23、OPC 规范的研究将非常具有现实意义。此外,对作为过程控制通信标准的 OPC 的研究,将促进我国在工业控制领域的发展。在国际化过程中,中国已经成为“世界工厂”,各种先进的管理和控制技术都在这个试验场中运用,我们应该抓住机遇,跟上世界发展的潮流,掌握和发展控制领域具有自主知识产权的核心技术,将对经济建设和国防现代化产生积极而深远的影响。第 6 页 124 论文的主要结构 本论文课题是研究基于 OPC 规范的数据通信,开发基于 COM 技术的 OPC 数据访问服务器。论文由理论综述和实际开发两部分组成,论文分五章,各章主要内容如下:第一章绪论部分简单介绍了 OPC 规范及其产生背景、发展历史、当前
24、 OPC技术研究和应用的现状。说明了论文的主要内容。第二章详细介绍了 OPC 规范的理论基础 COM 技术。列举了 COM 中的一些基本术语,分析组件创建的基本原理和技术细节,还介绍了 COM 的数据类型和内存管理等内容。第三章介绍了 OPC 的体系结构。包括它的框架结构和 OPC 规范的相关术语。第四章详细阐述了 OPC 通信的机制。包括 OPC 规范的对象与接口、OPC通信的方式、连接点技术和活动状态与数据源对数据采集影响的分析。第五章 OPC 服务器的总体设计。给出了 OPC 服务器中对象的总体结构以及数据结构。OPC 服务器的具体实现。详细介绍了实现服务器对象的一些关键细节。这是本论文
25、的一个重点。第 7 页 第二章 COM 概论 COMComponent Object Model(组件对象模型的缩写)是开发 OPC 客户/服务器的技术基础。在 OPC 软件开发中将大量涉及 COM 的基本概念和设计方法。本章主要介绍 COM 技术的基础知识和如何创建 COM 对象。21 COM 与 OPC 的关系 OPC 是 OLE 在工业控制领域的应用。OLE 是微软为应用程序的集成提供面相对象的机制。OLE 引入了复合文档的概念,所谓复合文档就是指在一个文档中包含了另外应用程序的对象。另一个数据集成机制是动态数据交换(DDE),DDE 可以支持数据链接,但所用的容器要用自己的编码来调整数
26、据格式。DDE 结构是相当脆弱的,建立在动态数据交换(DDE)上面的 OLE 1.0 存在一些限制。因此在OLE 2.0 中实用可靠的远程过程调用协议(LRPC)取代了 DDE,除了共享内存外,统一数据传输机制(UDT)提供了几种新的高效传输数据的方法,新的标记(moniker)机制完善了链接跟踪技术,这样 COM 就作为 OLE 2.0 的基础出现了,因而COM也就成了OPC的基础。在OLE 2.0 的发布后彻底加速了OPC的发展13。22 COM 技术 COM 作为广泛使用的组件工具,提供了丰富的集成服务,它是目前唯一可行的可复用、现成的客户机与服务器组件工具。组件(component)就
27、是一个软件模块,专门用来完成特定的工作,任何程序都可以使用组件。COM 是构造二进制兼容软件组件的规范,也就是说 COM 不是编程语言、代码库,而仅仅是一个二进制规范,可以建立能够相互通信的组件。因此,COM 是与语言无关的,只要按照 COM 规范,不论使用 VC、VB、java 还是 Delph 实现的代码,因其二进制兼容它们都能够 第 8 页 很好的在一起工作14。接口接口(Interface)接口具有使任何程序能访问该组件的功能。组件正是通过暴露给外界的接口,才能够让不同的软件使用该组件,它是组件中的最小单元。组件功能通过虚拟函数表(virtual function table)访问,也
28、称 vtable 或 VTBL。vtable 不能包含实际函数,只是包含组件函数的一组指针。组件要访问其他组件的功能时,要通过这个 vtable,客户不能直接访问 vtable。接口指针处于客户与 vtable 之间,通过接口指针,客户机见到了 vtable 表中指针的指针。此外,接口在发布之后,与某个 IID 之间的绑定是不能改变的,需要修正该接口时,可以使用聚合或者包容的方法来生成新的接口和新的接口 ID,这样,就能保证软件在接口发布之后能保持相同14。图 21 接口访问模型 GUID(Globally Unique Indentifier,全局唯一标识符全局唯一标识符)COM 接口和其他
29、实体需要有能够防止出现名称冲突的标识机制,开放软件基金会的分布计算环境(DEC)采纳的全局唯一标识符,它由算法生成可以确保唯一性的 128 位长度的字符串。可以使用 Windows 实用程序 GUIDGEN 来生成 GUID。IID 与 CLSID、LIBID 都是使用不同的 GUID 来表示定义的唯一性14。HRESULT 接口的几乎所有方法返回类型为 HRESULT(AddRef 和 Release 除外),它表示状态码。一个 HRESULT 是一组简单的 32 位数字,它也包含了结构的错误程序码。&m_pServer-AddGroup客户程序 PwpSimuOPC.dll 接口指针 IO
30、PCServer 虚函数表指针 IOPCServerOPCServer(vtable)IUnknown 第 9 页 因为 COM 远程进程要向调用者返回 RPC 错误,所以 COM 接口函数最好设置返回类型为 HRESULT,若非如此,则 COM 没有办法告诉方法调用者服务器崩溃或者网络中断等特定错误码。第 31 位 S 用来指示成功或者失败,其内容为 SERVERITY_SUCCESS(0)或者SERVERITY_ERROR(1),可以使用 SUCCEEDED()或者 FAILED()函数来判断到底该 HRESULT 是成功还是失败。当 Facility 被用来对相关的状态程序代码做分群时,
31、第 30 位到 27 位被保留,Facility 表示功能号(发生错误的区域)。低 16 位 Code 用来编码,进行失败或者成功的说明。在自定义接口错误码时,需要使用功能码FACILITY_ITF,这些错误代码对于特定的接口来说是唯一的。HRESULT 命名规则为:_ IUnknown 接口接口 COM 中所有的接口都是直接或者间接从特定的 IUnknown 接口中派生的,所以,COM 程序除了自己的方法外,还需包括三个 IUnknown 的方法15。Interface IUnknown HRESULT QueryInterface(REFIID iid,void*ppvobject);UL
32、ONG AddRef();ULONG Release();第一个方法支持接口协商,使用接口协商的客户程序可以找到附加的接口;其余两个方法支持引用计数,客户使用引用计数来控制对象的生存期。23 创建 COM 组件 S R C 31 30 29 16 15 0Faclity Code 图 22 HRESULT 结构 第 10 页 在 COM 组件与客户程序通信时,不是直接在客户进程中创建组件对象,再对组件进行调用,而是在函数 DllGetClassObject()中首先返回一个类厂对象,然后由类厂对象创建特定 CLSID 的组件。类厂管理组件的创建和装载、卸载 DLL。类厂本身也是一个支持特定接口
33、 IClassFactory 的 COM 对象,COM 运行系统可以查询该接口。概括地说,类厂是生成对象的对象。IClassFactory 有两个方法:16 CreateInstance 该方法实例化一个对象并返回该对象的指针。LockServer 该方法可以提供单独的“锁定计数”。锁定计数可以在没有对象实例的情况下仍将服务器留在内存中,从而改善系统的性能。231 建立信道 在下面的流程图中使用 OPC 客户为 COM 客户程序,OPC 服务器为 COM 组件,进行通信信道建立的说明。在流程控制图中:实线表示相应的元素已经被建好,并且是活动的;虚线则表示相应的元素不再是活动的;线上分得灰色条块
34、表示每个操作的生命期;水平线表示将流程控制从一个结构化元素传递给下一个元素的函数调用。图 23 信道建立流程 New COPCServerNew COPCFactoryDllGetClassObjectCoGetClassObjectOPC客户COM 库 DLL类 厂 OPC 服务时 间 CoCreateInstance IClassFactory:CreateInstance(IID,I_OPCServer)IClassFactory:Realease()PI_OPCServerFx()第 11 页 一个 COM 客户程序要启动 COM 组件,最基本的条件是要知道这个 COM 组件的 CLS
35、ID 以及它所支持接口的 IID。在启动 COM 组件之前,必须根据这个 COM组件的线程安全等级来声明要使用这个 COM 组件的线程到底是属于哪一种线程模 式,通 常 使 用CoInitializaEx(NULL,COINIT_MULITITHREADED)或 者CoInitializa(NULL)。使用后者除了声明线程模型之外,同时也对 COM 的函数库进行初始化。接着客户调用 COM 库中实现的 CoCreateInstance,CoCreateInstance 实际上在内部是用 CoGetClassObject 实现的。CoGetClassObject 将在注册表中查找指定的 OPC
36、服务器组件,找到之后,它将装载实现此服务器的 DLL(并不是每次在从零个实例化对象到一个实例化对象,而仅当 DLL 第一次被装载时 LockServer()锁定该组件)。装载成功后,它将调用在 DLL 服务器中实现的 DllGetClassObject。此 函 数 的 作 用 是 创 建 相 应 的 类 厂,可 以 使 用 New 操 作 符 实 现。另 外DllGetClassObject 还将查询 IClassFactory 接口,并将其返回给 CoCreateInstance。然后 CoCreateInstance 将使用此接口来调用 IClassFactory:CreateInstan
37、ce 函数,并查询 COM 服务器组件的某一接口,如 I_OPCServer。在得到了此接口之后,CoCreateInstance 将释放相应的类厂并将 I_OPCServer 指针返回给客户,然后 COM客户就可以使用此接口指针来调用 OPCServer 组件中的某个方法了。232 线程与进程 初始化 COM 库时,客户需要知道组件的线程类型,在 COM 中有其特定的线程模型,而所有线程都在某一进程中运行。在一个进程中可能包含多个线程,也可能在其中使用多线程模型。可以说,进程是线程的容器,所有的线程肯定总在某一进程中运行。COM 组件在运行时,根据与客户进程的关系,可以分成进程内服务器 DL
38、L、本地服务器 EXE 和远程服务器。DLL 驻留在与客户程序相同的进程中,这种服务器与客户不需进行调度就可直接通信,所以速度最快,但是,客户程序由于意外而崩溃时,将导致 DLL 服务器瘫痪。考虑到组件的运行速度和运行效率,COM 主要是以 DLL 服务器方式发布,在论文中的 OPC 服务器也是以 DLL 服务器的方式实现。本地服务器 EXE 与客户程序运行在同一台机器上,但处于不同的进程中,这样就避免了因客户程序崩溃而导致服务器瘫痪。由于进程间需要调度,就增加 第 12 页 了系统开销,但是该服务器以可执行文件方式存在,可以独立运行,且运行比较稳定。远程服务器在分布式网络中使用,该服务器使用
39、代理/占位程序,使不同地址空间的程序能够运行。在 COM 中,相对于进程而言,线程就复杂得多重要得多。使用 OLE 处理线程的缺省方式非常简单,OLE 对每个本地服务器或者远程服务器使用一个线程去处理所有客户对该服务器的请求。另一个可选的途径是使用“Apartment Model Threading”(公寓线程模型),在该模型下,所有向服务器的调用都将保证被串行化,公寓线程模型简化了在多个客户访问时的访问机制和代码复杂程度。单线程模型虽然简单安全方便,但这也正是其不利之处。它只有一个线程,所有调用都被串行化,就必须在运行时不能有延迟,如若有任何延迟,会阻止已被串行化的消息循环并抛弃另外的请求,
40、阻断所有客户对服务器的通信。所以在OPC 服务器的开发中一般不被采用,而使用公寓线程模型允许 COM 组件能够使用多线程,其中每个线程都有自己的 STA(single threaded apartment 单线程套间)。在这种应用中,驻留在一个线程中的客户可以访问在另一个线程中的对象(也就是驻留在另一个套间中的对象),但是这种访问必须要通过代理。代理使用消息队列同步机制实现了 COM 对象串行化访问。这种方法的优点是它能够使采用多线程应用中的 COM 类不用考虑自身方法的线程安全问题。然而,对于支持具有套间线程模型的类的 COM 服务器来说,就需要考虑多客户应用允许同时实例化多个对象时,如何保
41、护如对象计数器和锁定计数器之类的静态和动态数据并行访问的问题。COM服务器中使用方法DllGetClassObject和DllCanUnloadNow以及类厂的方法都必须是线程安全的。在大多数情况下,COM 底层将通过代理和存根程序自动处理调度问题而不需提供任何特殊编码。但在同一进程内跨越套间边界的操作可能会引发需要给予特殊关注的事件。在同一个进程中,一个全局指针可能会经常被某一代码段或者线程使用,但对接口指针有一个特例。它不能被同一个进程内的两个套间使用。只有拥有此接口指针的套间可以使用此指针(无论它是一个直接指向目标对象的指针或者指向接口代理的指针)。如果希望另一个套间使用此接口指针,就必
42、须将它列集后穿越。这是 COM 的一条定律。COM 提供了两个 API 函数来列集和解列一个接口指针。CoMarshalInterThreadInterfaceInStream 把一个接口指针调度到一个流对象 第 13 页 中。CoGetInterfaceAndReleaseStream 从流对象中将接口指针去掉并释放该流对象。CoMarshalInterThreadInterfaceInStream 是 CoMarshalInterface 的 简 写 版,CoMarshalInterface 可用于从一个进程将一个接口指针列集到另一个进程。而CoMarshalInterThreadInte
43、rfaceInStream 只可用于在同一个进程里把指针从一个套间列集到另一个套间,该 API 函数的任务仅仅是创建一个列集包并列集一个给定的接口。HRESULT CoMarshalInterThreadInterfaceInStream(REFIID riid,IUnknown *pUnk,IStream *ppStm );该函数采用了一个 IID,一个指向要列集的接口的指针,并返回一个指向IStream 接口的指针,此接口包含了列集和解列所需的所有信息。图 24 线程管理 Object STA1 STA2 CoMarshalInterThreadInterfaceInStream()ISt
44、reamCoGetInterfaceAndReleaseStream()Proxy 线程 A 线程 B 流对象 第 14 页 STA1 线程想将一个接口指针传递给另一个 STA 的线程。STA1 首先调用CoMarshalInterThreadInterfaceInStream 来获取存储在流对象内的独立于公寓接口展示。其第一个参数是正被调度接口的 IID,第二个参数是实现该接口的对象的指针。在第三个参数中返回的 IStream 指针存储在可以让 STA2 访问的全局变量内。STA2 将这个 IStream 指针传递给 CoGetInterfaceAndReleaseStream 函数来反调度
45、该流并获取指向代理的与公寓相关的接口指针,该指针应该可以在 STA2 中使用。当接口指针被反调度时,任何由 STA2 线程作出的对 STA1 中对象的调用现在都通过STA2 所装载的代理来完成17。与单线程模型不同,使用公寓中组件无需是线程安全的,COM 将同步对公寓中组件的访问,这些访问可以是来自公寓线程模型的,也可以来自自由线程模型的。从内部实现上讲,COM 将使用一个隐藏的 Windows 消息队列来同步这种调用。这一点使得为公寓线程模型编写组件与编写窗口过程极为相似(消息循环的作用是同步对窗口过程的访问,COM 将使用同样的机制来同步对公寓线程模型的访问)。公寓线程模型有其独特的性质。
46、首先,组件只能运行于一个线程中。此种组件只能供创建它的线程访问,由于组件只能被一个线程访问,它将在一个单线程环境中满足各种实际的需求,并需考虑同步的问题。但组件仍应保护其全局数据,因为同窗口过程一样,它是可重入的。其次,在跨越套间边界时必须对接口进行调度。从其他线程发来的调用请求必须被调度,以使它们像是从组件所在线程中发出的那样。由于一个套间只有一个线程,因此所有其他线程都在此套间之外。跨越套间边界的调用必须被调度。再次,DLL 入口点必须是类型安全的。公寓线程中的组件无需是线程安全的,因为它们只会被创建它们的线程所调用。但进程内服务器 DLL 中的入口点,如 DllGetClassObjec
47、t 和 DllCanUnloadNow 则必须是线程安全的。这是因为多个不同线程中的多个客户可能会同时调用它们。为使这些函数是线程安全的,需要同步对共享数据的访问。在某些情况下,这意味着类厂也可能需要是线程安全的。若为所建立的每一个组件都创建一个不同的类厂,那么这些类厂无需是线程安全的,这时因为同一时刻将只会有一个客户来访问它们。但若 DllGetClassObject 只创建一个类厂并用它来创建所有的组件实例,则将需要保证此类厂是线程安全的,因可能会有多个线程同时访问此类厂。总的说来,除非某一线程中将 COM 对象所支持接口的指针当作参数传给另一线程执行它的方法,否则公寓线程模型不需通过调度
48、,因此效率明显提升18。第 15 页 除了上述所涉及的两种模型外,还有自由线程模型以及 Both 模型等,由于在OPC 服务器的开发中没有使用,这里就不再论述。当使用多线程时,就必须编写线程安全的应用程序。也就是说,必须让各个线程同步。在 NT 内核的操作系统中使用四种同步对象:事件、互斥变量、信号量和临界区。事件同步对象可以让一个线程通知另一个线程或其他多个线程,现在可以执行特定的任务了。提供 7 个 API 函数来创建和使用事件同步。互斥变量用于控制对资源的存取。与事件一样,互斥变量使用 WaitForSingleObject()和 WaitFor-MultipleObjects()函数等
49、待互斥变量的所有权。信号量也是用于控制对资源的存取。和事件一样,信号量也使用 WaitForSingleObject()和 WaitForMultipleObjects()函数等待互斥变量的所有权17。互斥变量只允许一个客户存取控制的资源,而信号量允许一个或多个(有一个明确的上限)客户同时存取控制的资源。临界区是轻量级的互斥变量。与互斥变量一样,它也控制对资源的存取。临界区与互斥变量的不同之处是互斥变量是内核对象而临界区是某个进程内部的。如果要控制跨进程的资源,应该使用互斥变量,否则,轻量级的临界区是更好的选择。233 实现 QueryInterface()在 COM 信道建立,客户端程序获得
50、一个指向组件接口的指针时,客户可以在那个接口上调用 QueryInterface(),请求另外一个不同的接口。在使用 QueryInterface()函数时需遵循三个基本规则:19 由一个组件对象支持的一组接口不能在运行时改变;已知任何一个由组件对象支持的一组接口中的任何接口,客户端程序可以成功地请求到其中的任何接口;如果请求某一接口返回成功,那么再一次在同样的组件对象上请求同样的接口,该接口必须返回相同的指针。概括来说就是:对称性、反身性和传递性。COM 不提供使客户端程序可以请求组件所支持接口列表的方法。客户必须在它请求接口之前,知道要请求哪个接口。客户不能臆断组件接口的构成,只能通过 Q