网站出现的的问题,天津工程信息建设网,淄博网站建设乐达推广,公司建设网站费用做那个科目面向对象的思想难以适应这种分布式软件模型#xff0c;于是组件化程序设计思想得到了迅速的发展。 按照组件化的程序设计的思想#xff0c;复杂的应用程序被设计成一些小的#xff0c;功能单一的组件模块#xff0c;这些组件模块可以运行在同一台机器上#xff0c;也可以运…面向对象的思想难以适应这种分布式软件模型于是组件化程序设计思想得到了迅速的发展。 按照组件化的程序设计的思想复杂的应用程序被设计成一些小的功能单一的组件模块这些组件模块可以运行在同一台机器上也可以运行在不同的机器上。 为了实现这样的应用软件组建程序和组建程序之间需要一些极为细致的规范 只有组件程序遵守了这些共同的规范然间系统才能正常运行。 为此OMG和Microsoft分别提出了CORBA(Common Object Request Breaker Architecture)和COM(Component Object model)标准目前CORBA模型主要应用于UNIX操作系统平台上而COM 则主要应用于Microsoft Windows操作系统平台上。 在COM标准中一个组件程序也被称为一个模块它可以是一个动态连接库(DLL), 被称为进程内组件(in-of-process component)也可以是一个可执行程序(EXE)被称为进程外组件(out-of-process component). COM对象是建立在二进制可执行代码级的基础上而C等语言中的对象是建立在源代码级基础上的因此COM对象是语言无关的。这一特性使用不同编程语言开发的组件对象进行交互成为可能。 在Microsoft Windows系统平台上COM技术被应用于系统的各个层次从底层的COM对象管理到上层的应用程序交互都用到了COM标准。 概述 COM既提出了组件之间进行交互的规范也提供了实现交互的环境 因为组件对象之间交互的规范不依赖于任何特定的语言所以COM也可以是不同语言协作开发的一种标准。 OLE技术以COM规范为基础OLE充分发挥了COM标准的优势使Windows操作系统上的应用程序具有极强的可交互性。如果没有OLE的支持Windows操作系统则会逊色很多。 但是COM规范并不局限于OLE技术实际上OLE技术只是COM的一个应用而已这几年OLE技术在进行网络互连是显示出了很大的局限性而COM则表现出了极强的适应能力。 COM标准包括规范和实现两大部分规范部分定义了组件和组件之间通信的机制这些规范不依赖于任何特定的语言和操作系统只要按照该规范任何语言都可以使用; COM标准的实现部分是COM库COM库为COM规范的具体实现提供了一些核心服务。 COM是面对对象的软件模型因而对象是他的基本要素之一。类似于C中对象的概念对象是某个类(class)的一个实例;而类则是一组相关的数据和功能组和在一起的一个定义。使用对象的应用(或另一个对象)成为客户有时也成为对象的用户。 接口是一组逻辑上相关的函数集合其函数也被称为接口成员函数。对象通过接口成员函数为客户提供各种形式的服务。 在COM模型中对象本身对于客户来说是不可见的客户请求服务时只能通过接口进行。每一个接口都由一个128位的全局唯一标识符(GUID,Globally Unique Identifier)来标识。客户通过GUID获得接口的指针在通过接口指针客户就可以调用其相应的成员函数。 一般来说接口是不变的只要客户期望的接口在组建对象中还存在它就可以继续使用该接口所提供的服务。对象可以支持多个接口因此对组件对象的升级可通过增加接口的办法实现这样得到的新接口可以不影响老接口的使用。 客户如何来标识COM对象呢与接口类似每个对象也用一个128位GUID来标识称为CLSID(class identifier,类标识符或类ID)用CLSID标识对象可以保证(概率意义上)在全球范围内的唯一性。 只要系统中含有这类COM对象的信息并包括COM对象所在的模块文件(DLL或EXE文件)以及COM对象在代码中的入口点客户程序就可以由此CLSID来创建COM对象。 那么客户怎么使用COM对象所提供的服务呢客户获得的又是什么呢 实际上客户成功创建对象后它得到的是一个指向对象某个接口的指针因为COM对象至少实现一个接口所以客户就可以调用该接口提供的所有服务。 但是COM对象可以有自己的状态正是这种状态才使客户感觉到COM对象的存在。如果客户同时拥有两个相同CLSID的对象则两个对象可以有不同的状态客户完全不必关心COM对象是怎么实现的以及两个对象的状态数据之间有什么关系(数组或者链表)。当然COM对象也可以是无状态的这种COM对象以提供功能服务为主可以用来代替传统的API函数接口使得应用程序编程接口更为有序组织层次性更强。 COM本身除了规范之外也有实现的部分其中包括一些核心的系统级代码也正是这部分核心代码才使得对象和客户之间可通过接口在二进制代码级进行交互 。 在Microsoft Windows操作系统环境下这些库以.dll文件的形势存在其中包括以下内容: (1) 提供了少量的API函数实现客户和服务端COM应用的创建过程。在客户端主要是一些创建函数;而在服务器端提供一些对象的访问支持。 (2) COM通过注册表查找本地服务器即EXE程序以及程序名与CLSID的转换等。 (3) 提供了一些标准的内存控制方法使应用控制进程中内存的分配。 COM库一般不在应用程序层实现而在操作系统层次上实现因此一个操作系统只有一个COM库实现。而且COM库的实现必须依赖于具体的系统平台尤其是系统底层的一些标准。 COM库可以保证所有的组件按照统一的方式进行交互操作而且它使我们在编写COM应用时可以不用编写为进行COM通信而必需的大量基础代码而是直接利用COM库提供的API进行编程从而大大加快了开发的速度。例如现在COM库的版本都支持远程组件即分布式COM我们不用编写任何网络或者RPC(remote procedure call)的代码就可以实现在网络上进行程序之间的通信。 如果我们用面向对象语言来实现COM对象则很自然可以用类类定义对象。在C语言中对象的概念可能变成一个逻辑概念如果两个对象同时存在则在接口实现中必须明确知道所进行的操作是针对哪个对象的这个过程可由COM接口的定义保证。 COM规范使用GUID来标识COM对象的思想源于OSF(Open Software Foundation)采用的UUID(Universallz Unique Identifier), UUID被定义为DCE(Distributed Computing Environment)的一部分主要用于表识RPC通信的双方。 除了封装性和重用性C对象还有一个重要特性是多态性。正是C对象的多态性才体现了C语言用类描述事物的高度抽象的特征;COM对象也 具有多态性但这种多态性需要通过COM对象所具有的接口才能体现出来就像C对象的多态性需要通过其(virtual)函数才能体现一样。 从API到COM接口 假如我们要实现一个字处理应用系统它需要一个查字典的功能按照组件化程序设计的方法自然应该把查字典的功能放到一个组件(.dll)程序中实现。如果以后字典程序的查找算法或者字典库改变了只要应用程序和组件之间的接口不变则新的组件程序仍然可以被应用系统使用。这就是采用组件程序带来的灵活性。 为了把应用系统和组件程序连接起来又能使它们协同工作最简单的做法就是先定义一组查字典的函数而且这组函数尽可能一般化不要加入特定的与字典库相关的知识。 函数 功能说明 Initialize 初始化 LoadLibrary 装入字典库 InsertWord 插入一个单词 DeleteWord 删除一个单词 LookupWord 查找单词 RestoreLibrary 把内存中的字典库存入指定的文件中 FreeLibrary 释放字典库 平面型的API接口层可以很好地把两个程序连接起来但存在以下一些问题 (1) 当API函数非常多时使用会非常不方便需要对函数进行组织。 (2) API函数需要标准化按照统一的调用方式进行处理以适应不同的语言编程实现。参数的传递顺序参数类型寒暑返回处理都需要标准化。 COM定义了一套完整的接口规范不仅可以弥补以上API作为组件借口的不足还充分发挥了组件对象的优势并实现了组件对象的多态性。 接口定义和标识 从技术上讲接口是包含了一组函数的数据结构通过这组数据结构客户代码可以调用组件对象的功能。接口定义了一组成员函数这组成员函数是组件对象暴露出来的所有信息客户程序利用这些函数或的组件对象的服务。 客户程序用一个指向接口数据机构的指针来调用接口成员函数。接口指针实际上又指向另一个指针这第二个指针指向一组函数称为接口函数表(虚函数表)接口函数表中每一项为4个字节长的函数指针每个函数指针与对象的具体实现连接起来。通过这种方式客户只要获得了接口指针就可以调用到对象的实际功能。 对于一个接口来说他的虚函数表vtable是确定的因此接口的成员函数个数是不变的而且成员函数的先后顺序也是不变的;对于每个成员函数来说其参数和返回值也是确定的。 在一个接口的定义中所有这些信息都必须在二进制一级确定不管什么语言只要能支持这样的内存结构描述也就是能够支持“structure“或“record“类型并且这种类型能够包含双重的指向函数指针表的成员则它就可以支持接口的描述从而可以用于编写COM组件或者使用COM组件。 接口描述语言IDL COM规范在采用OSF的DCE规范描述远程调用接口IDL的基础上进行扩展形成了COM接口的描述语言。 COM规范使用的IDL接口描述语言不仅可用于定义COM接口同时还定义了一些常用的数据类型也可以描述自定义的数据结构对于接口成员函数我们可以指定每个参数的类型输入输出特性甚至支持可变长度的数组的描述。IDL支持指针类型与C/C很类似。 Microsoft Visual C提供了MIDL工具可以把IDL接口描述文件编译成C/C兼容的接口描述头文件(.h)。 IUnknown的定义(IDL): interface IUnknown{ HRESULT QueryInterface([in] REFIID iid, [out] void **ppv);ULONG AddRef(void);ULONG Release(void);} IUnknown的定义(C): class IUnknown{ Public:virtual HRESULT _stdcall QueryInterface([in] REFIID iid, [out] void **ppv)0;virtual ULONG _stdcall AddRef(void)0;virtual ULONG _stdcall Release(void)0;} 进程内组件 因为进程内组件和客户程序运行在同一个进程地址空间中所以一旦客户程序与组件程序建立起通信关系之后客户程序得到的接口指针指向组件程序中接口的vtable这个vtable包含了所有成员函数地址客户代码可以直接调用这些成员函数所以其效率非常高。 因为DLL程序是在运行时刻被客户装入到内存中的所以DLL模块本身也是独立的它并不依赖于客户程序。 在C语言中为了使编制的DLL程序更为通用一般指定DLL的引出函数使用_stdcall调用习惯如果使用了_cdecl调用习惯则有些编程语言环境就不能使用这些DLL程序。C编译器为DLL程序的每个引出函数生成了一个修饰名这些修饰名对于不同的编译器并不兼容因此从通用性角度出发我们在每个函数定义前加上extern ?C“ 说明符。在Visual C 开发环境中下面的说明语句可以很好的说明一个引出函数 extern ? C“ int _stdcall MyFunction(int n); 为了编制DLL程序我们可以按照这样的步骤 (1) 创建一个DLL工程 (2) 对每个引出函数使用extern ? C“说明符以及_stdcall修饰符如上面对MyFunction函数的说明。 (3) 按照传统的编程方法我们还应该编写一个DEF文件用来描述DLL程序的模块信息。在Win32平台上我们可以不使用DEF文件而是直接在函数说明时使用_declspec(dllexport)说明符例如 extern ? C“_declspec(dllexport) int _stdcall MyFunction(int n); 按照这样的方法建立起来的DLL模块可以被其他程序调用因为C连接器会把所有引出函数的信息连接到最终的目标代码中。 从客户程序一方来看有三个系统函数可用于操作DLL程序LoadLibrary, GetProcAddress, 和FreeLibrary。 一般地对于DLL程序的使用过程按照这样的步骤进行 首先客户程序使用LoadLibrary函数装入DLL该函数返回模块的实例句柄供以后操作该模块使用。 然后客户程序可以调用GetProcAddress函数获得DLL中引出的函数的地址我们既可以按函数的序号(在DEF文件中指定)也可以按函数的名字来获取引出函数的地址因为客户程序和DLL程序在相同的内存地址空间中所以客户程序可以直接调用这些引出函数。 最后FreeLibrary把DLL程序卸出内存以便释放资源。 说明 (1) DLL程序不仅可以引出函数也可以引出全局变量因为客户程序和DLL程序在同一个地址空间所以把DLL中的全局变量引出到客户程序中是有意义的。引用的方法并不复杂或者把变量名放到DEF文件的EXPORTS部分并加上DATA选项; 或者在变量说明前面加上_declspec(dllexport)说明符。 (2) DumpBin 通过/Exports选项可以列出DLL程序中的所有被引出的信息。 (3) 客户程序本身也可以是一个DLL程序但它一定先被装入到进程空间中以便可以调用系统函数操作作为服务程序的DLL模块。 进程外组件 因为进程外组件程序和客户程序位于不同的进程空间之中他们使用不同的地址空间所以组件和客户之间的通信必须跨越进程边界这就涉及到以下一些问题 (1) 一个进程如何调用另外一个进程中的函数 (2) 参数如何从一个进程被传递到另外一个进程中 Windows平台上在不同进程之间进行通信的办法很多包括DDE, named pipe,或者共享内存等等COM采用了LPC(Local Procedure Call)和RPC(Remote Procedure Call) RegEdit可检查CLSID子键下的COM对象(63页) Microsoft Visual C提供OleView.exe可列出当前机器上的所有类别信息以及每一种类别下的组件对象列表。 RegSvr32 D:\DicComp\DictComp.dll RegSvr32 /u D:\DicComp\DictComp.dll DLL组件必须有DllRegisterServer和DllUnregisterServer两个用于注册的入口函数才能用RegSvr32注册。 COM规定支持自注册的进程外组件必须支持两个命令行参数/RegServer和/UnregServer以便完成注册或注销操作。 Class Factory 实际上客户程序并不直接调用组件程序的引出函数它调用COM库的函数进行组件对象的创建工作COM库的创建函数根据注册表的信息调用组件程序的入口函数来创建组件对象。组件程序需要提供一个标准的入口函数DLLGetObjectClass用于提供本组程序的组件信息。 Class Factory和DLLGetObjectClass函数 类厂是COM对象的生产基地COM库通过类厂创建COM对象; 对应每一个COM类有一个类厂专门用于该COM类的对象创操作。类厂本身也是一个COM对象它支持一个特殊的接口IClassFactory其定义如下 Class IClassFactory : public IUnknown {virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID iid, void **ppv) 0;virtual HRESULT _stdcall LockServer(BOOL bLock) 0; }; 接口IClassFactory有一个重要的成员函数CreateInstance用于创建对应的COM对象。因为每个类厂之针对特定的COM类对象所以CreateInstance成员函数知道该创建什么样的COM对象。在CreateInstance成员函数的参数中第一个参数pUnknownOuter用于对象被聚合的情形没有聚合设成NULL。IClassFactory的另一个成员函数LockServer用于控制组建的生存周期。 因为类厂本身也是个COM对象它被用于其它COM对象的创建过程那么类厂对象又由谁来创建呢答案是DLLGetClassObject引出函数。DLLGetClassObject函数并不是COM库的函数而是由组件程序实现的引出函数我们先看一下DLLGetClassObject函数的原型 HRESULT DLLGetClassObject(const CLSID clsid,Const IID iid,(void **) ppv); COM库在接到对象创建的指令后它要调进程内组件的DLLGetClassObject函数由该函数创类厂对象并返回类厂对象的接口指针COM库或者客户一旦有了类厂的接口指针它们就可以通过类厂接口IClassFactory的成员函数CreateInstance创建相应的COM对象。 COM库与类厂的交互67页 在COM库中有三个API函数可用于对象的创建它们分别是CoGetClassObject, CoCreateInstance和CoCreateInstanceEx。通常情况下客户程序调用其中之一完成对象的创建并返回对象的初始接口指针。COM库与类厂也通过这三个函数进行交互。 原文转载自: http://www.cppblog.com/3522021224/archive/2007/06/22/26803.html转载于:https://www.cnblogs.com/hqbhonker/p/3430059.html