以前在我学校里培训过一段时间C++,我敬爱的吴老师略有提及。那个时候觉得COM遥不可及,觉得,哇塞好神圣。我觉得自己啥都没学好,我不应该这么早去涉及这片过于光荣的领地。既没有觉悟也没有动力去迎接这样一场学习。让对于COM的学习一拖再拖,就像拖延症。然而现实总是残酷的这项技术早已经不再神秘不再光荣依旧,技术的发展甩给我狠狠地一记巴掌,如果连这种技术都不了解确实很难混下去了。

•COM是微软组件对象模型的简称。由于COM具有二进制代码共享的特性,所以它具备了高可开发性、高度可维护性和高度的可移植性(跨开发语言),以至于在Windows上面的诸多应用软件采用了COM来做整体的架构。比如微软的DirectX等。COM虽然流行于2000-2004年之间,由于它的普及面之广,应用软件种类之繁多再加上Windows对其默认支持很好,开发出来的软件无需依赖其他的开发包,所以被很多软件公司采用至今。作为一个VC++程序员,是否系统掌握COM的用法成为是否合格的重要的衡量指标之一。
•下面我简单地讲解COM组件的三个优点。
•采用COM组件架构我们的软件,会使我们更方便地进行模块划分,而且各模块独立性高,耦合度低,从而更方便地进行开发任务的分工。(开发性)
•采用COM组件架构我们的软件,会使我们更方便地维护、升级软件,因为我们可以很方便地直接用新模块替换旧模块,而不影响软件的其它功能。(维护性)
•采用COM组件架构我们的软件,可以使我们已编写好的功能模块可以很方便地移植到其它平台,如从C++的MFC平台移植到C#的WinForm平台。因为COM组件是跨应用的,可以被C++调用也可以被C#调用。(移植性)
C++程序中的组件与接口:
•接口,是一种约定,一种协议。它是抽象的,指明了具体含义,但却没有实现这个定义。我们看一下C++的纯虚函数:求最大公约数,virtual int GreatestCommonDivisor(int a, int b) = 0;  //求a与b的最大公约数。这个函数的定义很明确,但没有实现这个含义的具体方法,所以,是抽象的。
•我们一般采用interface这个英文单词表示C++中的接口,它在Microsoft Visual Studio 安装目录\VC\PlatformSDK\include\objbase.h中被预定义。

#define   interface   struct

在其它开发平台下,也可以自己编写预定义代码

COM组件与COM接口:

•COM的定义:是Component Object Model (组件对象模型)的缩写
•COM组件是可以以二进制的形式发布,具有指定规则的二进制结构;
•COM组件是可以被其它应用程序来调用,以实现二进制代码的共享(跨应用);
•COM组件是完全与编程语言无关的。(跨语言);
•COM组件只能被运行在Windows操作系统平台上面,Linux,Mac不能适用。
•COM组件的内存结构和C++编译器为抽象基类所生成的内存结构是相同的。因此可以用C++的抽象基类来定义COM接口。
•COM组件必须继承于最基本的COM接口: IUnknow。
•IUnknow有三个函数,为别是QueryInterface, AddRef, Release。
 interface IUnknown
{
virtual HRESULT QueryInterface(const IID &iid, void **ppv) = ;
virtual ULONG AddRef() = ;
virtual ULONG Release() = ;
};

QueryInterface:

可以通过QueryInterface函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface返回一个指向此接口的指针。这里我们看到函数返回类型为HRESULT,参数其中一个的类型是const IID&。HRESULT跟IID是什么呢?

IID:
•IID,接口标识符,每个接口都可以设置一个IID,用于标志该接口,若标志了某个接口后,IID的值不能再修改。
•IID其实是: typedef GUID IID;
typedef struct _GUID
{
DWORD Data1; //随机数
WORD Data2; //和时间相关
WORD Data3; //和时间相关
BYTE Data4[]; //和网卡MAC相关
} GUID;

GUID:

•GUID有16个字节,共128位二进制数。
•GUID的生成方法,可以采用Windows SDK v6.0A的Tools文件夹下的GUID生成器生成。
•从理论上讲,它是不能保证唯一,但由于重复的可能性非常非常非常。。。非常小。有句夸张的说法是:“在每秒钟产生一万亿个GUID的情况下,即使太阳变成白矮星的时候,它仍是唯一的”
•GUID的表示方法:

// {0E04C466-6CE9-4513-B306-43E8F7025EB9}

static const GUID guid =

{ 0xe04c466, 0x6ce9, 0x4513, { 0xb3, 0x6, 0x43, 0xe8, 0xf7, 0x2, 0x5e, 0xb9 } };

QueryInterface的实现:

virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppv)
{ if (iid == IID_IUnknown)
{
//即使CA继承了两个IUnknown接口,其中一个来自于IX,另一个来自于IY。我们一般返回第一个被继承的IX接口。
*ppv = static_cast<IX*>(this);
}
else if (iid == IID_IX)
{
//返回IX接口
*ppv = static_cast<IX*>(this);
}
else if (iid == IID_IY)
{
//返回IY接口
*ppv = static_cast<IY*>(this);
}
else
{
//查询不到IID,*ppv返回NULL。
*ppv = NULL;
return E_NOINTERFACE; //函数返回值返回E_NOINTERFACE,表示组件不支持iid的接口。
} //查询成功时,需要自增引用计数
AddRef(); return S_OK; //返回S_OK
}

引用计数的原理:

•引用计数技术就是用来管理对象生命期的一种技术。
•对象O可能同时被外界A,外界B,外界C引用。也就是说外界A,外界B,外界C可能都在使用对象O。
•每次当对象被外界引用时,计数器就自增1。
•每次当外界不用对象时,计数器就自减1。
•在计数值为零时,对象本身执行delete this,销毁自己的资源。
•引用计数使得对象通过计数能够知道何时对象不再被使用,然后及时地删除自身所占的内存资源。
•IUnknown接口的AddRef与Release就是引用计数的实现方法。
AddRef和Release的实现:
 virtual ULONG STDMETHODCALLTYPE AddRef()
{
//简单实现方法
return ++m_lCount; //多线程编程采用如下方法,这种方法确保同一个时刻只会有一个线程来访问成员变量
//return InterlockedIncrement(&m_lCount);
} virtual ULONG STDMETHODCALLTYPE Release()
{
//简单实现方法
if (--m_lCount == )
{
delete this; //销毁自己
return ;
}
return m_lCount; ////多线程编程采用如下方法,这种方法确保同一个时刻只会有一个线程来访问成员变量
//if (InterlockedDecrement(&m_lCount) == 0)
//{
// delete this; //销毁自己
// return 0;
//}
//return m_lCount;
}

引用计数的优化:

•这种优化可行吗?答案是可行的!因为这种优化符合了引用计数优化的“局部变量原则”
•引用计数的优化原则:

一、输入参数原则:输入参数指的是给函数传递某个值的参数。在函数体中将会使用这个值但却不会修改它或将其返回给调用者。在C++中,输入参数实际上就是那些按值传递的参数。对传入函数的接口指针,无需调用AddRef与Release

二、局部变量原则对于局部复制的接口指针,由于它们只是在函数的生命期内才存在,因此无需调用AddRef与Release

•输入参数原则:
  void Fun(IX *pIXParam)     //参数传递存在赋值过程
{
//pIXParam->AddRef(); //可优化,注释掉
pIXParam->Fx1();
pIXParam->Fx2();
//pIXParam->Release(); //可优化,注释掉
}
•局部变量原则:
 void Fun(IX *pIX)
{
IX *pIX2 = pIX;
//pIX2->AddRef(); //可优化,注释掉
pIX2->Fx1();
pIX2->Fx2();
//pIX2->Release(); //可优化,注释掉
}
•以下代码可以优化吗?:
void  Fun(IX **ppIX)
{
(*ppIX)->Fx1();
(*ppIX)->Fx2();
(*ppIX)->Release(); //可以优化吗?
*ppIX = m_pIXOther;
(*ppIX)->AddRef(); //可以优化吗?
(*ppIX)->Fx1();
(*ppIX)->Fx2();
}
•答案是否定的!因为它不是输入参数原则,而是输入-输出参数原则。此原则下,引用计数不能优化!

//以上两句务必要运行,因为*ppIX 与m_pIXOther不一个属性同一个组件。

//比如假设*ppIX是指向第一次的new CA(),而m_pIXOther却是指向第二次的new CA()。

//或者*ppIX是指向new CA(),而m_pIXOther是指向new CZ(),CA与CZ的共同点,只是都继承了IX接口而已。

•引用计数,带来了高效的内存资源管理方法,能及时地释放不再使用的资源。但却带来了编码的麻烦。在后续的讲解中,会讲到对引用计数的封装,也就是智能指针,到时组件的客户不再编写AddRef与Release代码,也不需要编写delete代码,便可以方便,舒心地进行内存资源的管理。
 
 
 
 
 
 
 
 
 

COM初体验的更多相关文章

  1. .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验

    不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...

  2. Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验

    Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...

  3. Spring之初体验

                                     Spring之初体验 Spring是一个轻量级的Java Web开发框架,以IoC(Inverse of Control 控制反转)和 ...

  4. Xamarin.iOS开发初体验

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAA+CAIAAAA5/WfHAAAJrklEQVR4nO2c/VdTRxrH+wfdU84pW0

  5. 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...

  6. 【Knockout.js 学习体验之旅】(1)ko初体验

    前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...

  7. 在同一个硬盘上安装多个 Linux 发行版及 Fedora 21 、Fedora 22 初体验

    在同一个硬盘上安装多个 Linux 发行版 以前对多个 Linux 发行版的折腾主要是在虚拟机上完成.我的桌面电脑性能比较强大,玩玩虚拟机没啥问题,但是笔记本电脑就不行了.要在我的笔记本电脑上折腾多个 ...

  8. 百度EChart3初体验

    由于项目需要在首页搞一个订单数量的走势图,经过多方查找,体验,感觉ECharts不错,封装的很细,我们只需要看自己需要那种类型的图表,搞定好自己的json数据就OK.至于说如何体现出来,官网的教程很详 ...

  9. Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验

    Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出E ...

  10. Docker初体验

    ## Docker初体验 安装 因为我用的是mac,所以安装很简单,下载dmg下来之后拖拽安装即可完成. 需要注意的就是由于之前的docker是基于linux开发,不支持mac,所以就出现了docke ...

随机推荐

  1. Ajax结合Js操作灵活操作表格

    Table页面: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head& ...

  2. jQuery 尺寸

    通过 jQuery,很容易处理元素和浏览器窗口的尺寸. jQuery 尺寸 方法 jQuery 提供多个处理尺寸的重要方法: width() height() innerWidth() innerHe ...

  3. 数据库学习(整理)----6--Oracle如何快速备份和多次备份数表数据

    1.说明:  这里假设一种应用场景! 假设,银行系统中有大量的数据需要及时备份,如何才能快速高效呢! 条件需求: (1).不能设置同步锁(设置的会影响银行正常业务进行!使得银行系统处于维护状态,这是不 ...

  4. QT UI 使一个QWidget里面的元素自动填充满本QWidget

    使一个QWidget里面的元素自动填充满本QWidget: 对象查看器,右键点击本QWidget,选择"布局",为此QWidget增加一个布局. 如果该QWidget只有一个对象, ...

  5. #Leet Code# Root to leaf

    语言:Python 描述:使用递归实现 def getList(self, node): if node is None: return [] if node.left is None and nod ...

  6. MATLAB r2014a 下载+安装+激活

    MATLAB r2014a,下载包就有7个多GB,装完占用9个多GB,慎装.界面还不错,稍有改良. 其实本文是下载+安装+破解啦.读书人的事,怎么能叫破解呢?所以我这里讲的是如何激活啦. MATLAB ...

  7. hexdump——Linux系统的二进制文件查看工具

    hexdump是Linux下的一个二进制文件查看工具,可以将二进制文件转换为ASCII.10进制.16进制或8进制进行查看. 首先我们准备一个测试用的文件test,十六进制如下: 00 01 02 0 ...

  8. C51应用 Modbs Rtu协议实现与KEPServerEx 通信

    最近一客户要求使用STC12C5A60S2实现Modbus Rtu协议与KEPServerEx V4.0软件通信,采集单片机P2口每位的状态,设置P0口每位的状态,实现三路AD转换其中一路采集的是C0 ...

  9. -Xms -Xmx -Xmn -Xss -XX:

    这两天遇到了pergen space的问题,在晚上查了查发现还挺普遍,并且通过eclipse启动,通过bat启动或者linux下通过sh启动,处理方式是不一样的,不过都是调整jvm的大小 如果有遇到同 ...

  10. CSS3随笔系列之transform(一)—— transform-origin

    transform-origin属性平时似乎用得很少,它决定了变换时依赖的原点.基本的属性特性可以参考CSS手册. 如果在H5动画项目中,用到旋转的话,它还是不能小觑的. 假如我们做一个秋千效果 其实 ...