【COM原理和应用】3、COM的实现

1、进程内组件和进程外组件

使用dll实现组件程序,则客户长须在调用组件程序的服务时,需要将dll装进自身的进程,所以客户程序和组件运行于同一进程空间,此类组件称为进程内组件。使用exe程序的组件,在被调用时自身具有进程空间,因此客户程序和组件运行在不同的进程空间,此类组件称之为进程外组件。

1.1、进程内组件

在客户程序与组件简历链接之后,客户程序可以得到直接指向组件接口虚函数表的接口指针,因此可以直接调用组件的成员函数,效率很高。DLL文件本身独立于客户程序,它在运行时被装入客户程序的内存,可以被多个进程使用。

DLL程序包含一个引出函数表,包含函数名、函数序号和函数地址,客户进程在动态加载dll时简历一张表,将客户调用同DLL里函数地址链接起来。只要不修改DLL引出函数的名称或参数信息,那么即使修改并重建了DLL程序,也不需要重新修改客户程序。为了增强dll的通用性,通常DLL的引出函数使用_stdcall调用习惯,而且每个函数定义前面加上 extern "C"说明符。一个典型的DLL引出函数声明如下:

extern "C" int _stdcall SomeFunction(int n);</span>

COM组件的dll工程中还需要DEF文件来描述DLL程序的模块信息,在“LIBRARY”部分指定DLL的文件名,在"EXPORTS"部分列出所有的引出函数并给每个函数分配一个唯一的序号。或者,可以直接在函数生命是使用_declspec(dllexport)说明该函数是一个dll的导出函数:

extern "C" _declspec(dllexport) int _stdcall SomeFunction(int n);</span>

客户程序可以使用三个系统函数操作dll中的方法:LoadLibrary装载dll,GetProcAddress取导出地址的函数,FreeLibrary释放dll模块。一般地过程如:①客户程序私用LoadLibrary函数装载dll,返回模块的实例句柄共以后操作该dll模块使用;②客户程序调用GetProcAddress获得dll引出函数的地址,可以按函数序号或函数名获取;③DLL使用完成后,调用FreeLibrary释放DLL模块。另外,除了引出函数之外DLL还可以引出全局变量。

1.2、进程外组件

进程外组件的存在形式为一个exe可执行文件,该文件执行时会独占一个进程。客户进程如何加载进程外组件涉及到进程间通信等问题。在视频处理应用尤其是Directshow中此类组件使用较少,暂且略去以后有需要再来研究。

2、通过注册表管理COM对象

注册表是系统级的信息存储,客户程序和组件都可以访问。组件的注册指组件程序将其实现的COM对象的信息以及接口信息保存到注册表中,具有这种自注册能力的组件称为可自注册组件。

2.1、注册表结构

COM标准规定,注册表必须包含COM库在完成各种操作时所要求的各项信息。Windows系统中的注册表是一个巨大的树形结构,在一个根节点下包含了一些键key和值value,每一个键又包含子键和值,如此层层向下延伸,形成了树形层次结构。

2.2、COM组件注册表信息

COM组件所使用的注册表节点主要是HKEY_CLASSES_ROOT这个键,其中最主要的是CLSID子键。在该子键下列出了当前系统中已经注册的所有组件的信息。CLSID下面的每个子键都代表了一个COM组件,这个组件的路径是调用组件时最关键的问题。对于进程内组件,则组件子键下包含InprocServer32子键,其缺省值就是dll文件的完整路径。对于进程外组件,则组件子键下包含LocalServer32子键,其缺省值就是dll文件的完整路径。除了CLSID外,还可以将COM组件以字符串化的组件名来查找,组件名被保存于ProgID子键中。

除了CLSID子键之外,HKEY_CLASSES_ROOT键还有Interface子键保存了当前系统中COM接口的信息,以及TypeLib子键保存当前系统中类型库的信息。

2.3、COM组件的注册

进程外组件以可执行文件的形式存在,可以直接执行,所以可以在执行过程中完成自身注册;而进程内组件不能直接运行,必须被某个进程调用才能获得控制。Windows提供了专用于注册进程内组建的工具regsvr32.exe,只要dll提供了入口函数DllRegisterServer和DllUnregisterServer函数,regsvr32.exe就可以实现组件的注册或注销。注册和注销操作不是由Regsvr32.exe提供的,而是由两个入口函数实现。

3、类厂

客户程序不会直接调用组件dll的引出函数,而是调用COM库的函数进行组件对象的创建。组件程序提供的标准入口函数DllGetObjectClass用于提供该组件程序的组件信息。

3.1、类厂和DllGetObjectClass函数

杜宇每一个COM类,都有一个专门的COM对象用于该类实例的创建。这个特殊的对象即是COM类的类厂,支持一个特殊的接口IClassFactory。该接口中有一个重要的成员函数CreateInstance用于创建对应的COM对象。声明方式如:

class IClassFactory: public IUnknown
{
    virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0;
    virtual HRESULT _stdcall LockServer(BOOL bLock) = 0;
}

COM标准规定,每一个COM类都应对应一个类厂,如果一个COM组件中包含多个COM对象,则应由多个类厂。

COM组件的导出函数DllGetObjectClass实现创造类厂的功能,该函数的声明类似于:

HRESULT DllGetClassObject(const CLSID &clsid, const IID& iid, (void**)ppv);

该函数的第一个参数为待创建对象的CLSID,第二个和第三个参数用于保存接口IID和类厂的指针接口。

3.2、COM库与类厂的交互

COM库中包含三个API用于对象的创建,分别为CoGetClassObject,CoCreateInstance,CoCreateInstanceEx。通常客户程序调用这三个函数之一完成对象的创建并返回对象接口的指针。COM库和类厂也通过这三个函数进行交互。

(1)CoGetClassObject:函数的声明如下:

HRESULT CoGetClassObject(const CLSID &clsid, DWORD dwClsContext, COSERVERINFO *pServerInfo, const IID &iid, (void**)ppv);

该函数首先找到由CLSID指定的类的类厂,如果是进程内组件则调用DLL模块的DllGetClassObject函数,并把clsid、iid和ppv传入创建类厂并返回类厂对象接口指针。第二个参数dwClsContext指定组件的类别(进程内/外组件、进程内控制对象)。第三个参数pServerInfo为指定服务器信息,对进程内组件应为null。

(2)CoCreateInstance:函数声明如下:

HRESULT CoCreateInstance(IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv);

该函数是对CoGetClassObject的一层封装,内部实际调用了CoGetClassObject和生成类厂的CreateInstance函数完成对COM对象的创建。仅适用于创建本地COM组件。该函数也是实际使用中最常用的方法。

(3)CoCreateInstanceEx的功能与CoCreateInstance类似,只是可以用来创建远程COM组件。

3.3、类厂对组价生存期的控制

通常,类厂对象并不会长期保留,而是在每次创建对象的过程中建立新的。如果客户程序需要将类厂对象长期保留,那么可以调用IClassFactory接口的LockServer方法来锁定和解锁类厂。如果需要锁定类厂对象,则调用LockServer(TRUE);如果需要解锁,则调用LockServer(FALSE)。

4、COM库

4.1、COM库的初始化

是哦那个COM库的函数之前必须对COM库进行初始化,方法为:

HRESULT CoInitialize(IMalloc *pMalloc);

其中的参数指定一个内存分配器,通常情况下可以设置为NULL。通常一个进程只会对COM库初始化一次。在程序退出之前,必须对COM库进行反初始化,方法为:

void CoUninitialize(void);

4.2、组件程序的加载和卸载

(1)进程内组件的加载:在CoCreateInstance中,会调用CoGetClassObject。COM库根据注册表中的信息,找到相应CLSID对应的组件dll文件的完整路径,然后调用LoadLibrary,并调用dll中的DllGetClassObject导出函数创建相应的类厂,并返回IClassFactory接口,此时CoGetClassObject任务完成。然后客户程序调用类厂对象的CreateInstance函数负责COM对象的创建。

(2)进程内组件的卸载:当组件程序满足以下两个条件时才能被卸载:组件中对象数为0,类厂的锁计数器为0。此时DllCanUnloadNow返回TRUE。客户程序在空闲处理中调用CoFreeUnusedLibraries函数,该函数会检测当前进程中所有组件程序,当发现某个组件的DllCanUnloadNow返回TRUE时,将会调用FreeLibrary函数将组件从内存中卸载。

时间: 2024-09-03 15:53:17

【COM原理和应用】3、COM的实现的相关文章

路由器QOS功能原理和工作方式

设置路由器时,大多会用到路由器的安全机制,也就常说的QOS功能,QOS功能可以保护整个网络的安全,本篇带你了解其具体的原理和工作的方式. 一.QOS用来解决带宽解决网络延迟和阻塞等问题的一种技术,一般里面包含优先级别.弹性带宽管理等等,主要用来解决各种网络的攻击和病毒,保护网络的正常运行,它主要有以下几个方面的功能: 1.端口优先:可针对源端口.目的端口进行设置优先的级别,一般来说如果是玩游戏为主.那么我可以针对一些主流游戏的端口.优先这些游戏的带宽. 2.IP/网段优先:可针对源IP.目的IP

PhotoShop中正片负片叠底的原理介绍

关于正片叠底,正片,负片,通道,色相,色相环等等的相关理论一堆,大家可以从网上查到,原理就不讲了. 感觉单通道正片叠底效果应该属于填充色一类,但却与填充色又有很大的差异,与照片滤镜功能也有所差异,运用得当,最大的优点是在叠底后仍能保持比较好的照片通透度,而且简单易用,特别适合不太熟悉PS操作的朋友,此类方法运用广泛,配合起来使用比较方便,慢慢介绍吧. photoshop教程注:以下介绍的为RGB模式下的叠底,与CMYK模式下有所区别 方法一,单通道正片叠底 例一,叠出阳光色.提示:图片应尽量少漏

JavaScript 预解析的原理及实现

事实上或某种现象证明并不是这样的,通过<JavaScript权威指南>及网上相关资料了解到,JavaScript有"预解析"行为.理解这一特性是很重要的,不然在实际开发中你可能会遇到很多无从解析的问题,甚至导致程序bug的存在.为了解析这一现象,也作为自己的一次学习总结,本文逐步引导你来认识JavaScript"预解析",如果我的见解有误,还望指正. (1) 如果JavaScript仅是运行时自上往下逐句解析的,下面的代码能正确运行是可以理解的,因为我们

控件-mscomm串口波形绘制范例,求大神解析这三个函数,急急急,绘制波形图的原理是什么,拜托了

问题描述 mscomm串口波形绘制范例,求大神解析这三个函数,急急急,绘制波形图的原理是什么,拜托了 //串口void CPort_testDlg::OnComm() { //if(stop)return; VARIANT m_input1; COleSafeArray m_input2; long lengthi; BYTE data[600]; CString str; int ai=0bi=0ci=0di=0; int sum=0; if(m_Comm.GetCommEvent()==2)

你应该知道的RPC原理

      在学校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服务消费方和服务提供方是本地调用关系. 而一旦踏入公司尤其是大型互联网公司就会发现,公司的系统都由成千上万大大小小的服务组成,各服务部署在不同的机器上,由不同的团队负责.这时就会遇到两个问题:1)要搭建一个新服务,免不了需要依赖他人的服务,而现在他人的服务都在远端,怎么调用?2)其它团队要使用我们的新服务,我们的服务该怎么发布以便他人调用?下文将对这两个问题展开探讨. 1 p

计算机网络原理相关面试问题

1.简单介绍OSI的七层网络模型,画图描绘,描述主要几层的各自作用.OSI(Open System Interconnect,开放系统互连)七层网络模型. TCP/IP四层模型和OSI七层模型 表1-1是 TCP/IP四层模型和OSI七层模型对应表.我们把OSI七层网络模型和Linux TCP/IP四层概念模型对应,然后将各种网络协议归类. 表1-1  TCP/IP四层模型和OSI七层模型对应表 OSI七层网络模型 Linux TCP/IP四层概念模型 对应网络协议 应用层(Applicatio

图片原理与优化 如何在网站设计中发挥更好的效果

中介交易 SEO诊断 淘宝客 云主机 技术大厅 前言:该文收集了前辈们的一些关于图片优化的技巧,在此收拢到一起,对于各个方法的优化原理做了一些研究,希望能给大家对于图片优化这一块起到抛砖引玉的作用. 提到图片,我们不得不从位图开始说起,位图图像(bitmap),也称为点阵图像或绘制图像,是由称作像素(图片元素)的单个点组成的.这些点可以进行不同的排列和染色以构成一副图片.当放大位图时,可以看见赖以构成整个图像的无数单个方块. 常见的格式中JPG.PNG.GIF亦属于位图,所以它们的数据结构大致相

懒人促进社会进步 - 5种索引的原理和优化Case (btree,hash,gin,gist,brin)

标签 PostgreSQL , 多列聚合 , gin , btree , n_distinct , 选择性 , 如何选择索引方法(hash,btree,gin,gist,brin) , 如何优化索引 , 相关性 背景 在广告行业,精准营销是一个较热的话题,之前写过一个案例,如何使用PostgreSQL的array类型和GIN索引实时圈人的场景. <万亿级营销(圈人)迈入毫秒时代 - 万亿user_tags级实时推荐系统数据库设计> 使用以上方法,程序需要作出一些调整(当然,如果用户原本就是Po

HybridDB PostgreSQL &quot;Sort、Group、distinct 聚合、JOIN&quot; 不惧怕数据倾斜的黑科技和原理 - 多阶段聚合

标签 PostgreSQL , Greenplum , JOIN , group by , distinct , 聚合 , 非分布键 , 数据倾斜 , 多阶段聚合 背景 对于分布式系统,数据分布存储,例如随机.哈希分布. Greenplum数据库支持两种数据分布模式: 1.哈希(指定单个.或多个字段) 2.随机分布(无需指定任何字段) 数据分布存储后,面临一些挑战: JOIN,排序,group by,distinct. 1.JOIN涉及非分布键字段 2.排序,如何保证输出顺序全局有序 3.gro

最通俗易懂的解读比特币相关原理

周末花时间看了一些比特币原理相关的资料,虽然不敢说把每个细节都完全搞懂了,不过整体思路和关键部分的主要原理还是比较明白.写一篇文章分享给大家.这篇文章的定位会比较科普,尽量用类比的方法将比特币的基本原理讲出来.这篇文章不会涉及算法和协议中比较细节的部分,打算后面会再写一篇程序员视角下的比特币原理,那里会从技术人员的视角对比特币系统中较为关键的数据结构.算法和协议进行一些讲解. 在这篇文章中我会给出一个虚拟的村庄叫"比特村",整个文章会以讲故事的方式,逐步告诉大家比特币提出的动机.解决了