一、COM介绍
1、定义
(Component Object Model)
COM是微软公司的最高级的,包罗万象的二进制通讯规范(也就是说是大家都要遵守的合同)。用于软件组件间跨进程,跨机器,和操作系统进行交互操作。COM是透明位置的。它可以在EXE,DLL或者远程机器上使用。
OLE是一个主要与用户界面相关的高级功能的集合。COM和OLE的概念界限原本就不清晰,总是容易混淆。
2、历史
OLE(Object Linking & Embedding )是1991年首次出现的(是WINDOWS3.1自带的)。OLE最初的含义是对象链接和嵌入。当时用DDE(动态数据交换)作为底层通讯协议。
1993,COM首次出现。微软推出OLE2.0,开始用COM代替DDE作为底层通讯协议。这也是COM第一个重要的用途。
1996年,大多数开发人员开始编写32位的WIN95应用程序。他们发现,OLE使用COM的方式是一种非常好的设计软件的方法。开发人员开始使用类似的方法编写自己的对象和界面。另外,操作系统也开始要求使用COM技术编程,如编写WIN95用户界面。这些即不是OLE,也不是AUTOMATION,那么他到底是什么呢?这个属于大多数人倾向于使用COM。
3、发展
1996年,微软推出NT4.0,DCOM首次出现,作为NT的一部分。它实现了将COM在分布式系统中的应用。
1997年开始流行ATL。COM作为一种技术规范,最早是由C语言来实现的,但是实现起来比较复杂。出现VC以后,又对COM进行了预制和封装,大大简化COM应用的开发。这就是ATL(Active Template Library)。
4、现状
我们经常见到的用途:
使用外来控件。特别是在网页上使用ACTIVEX控件。ADO
WORD/EXCEL的应用。(两者交叉使用,在应用程序中调用)
二、概念
接口:可以理解为一个抽象的类。
OBJCET(component),相当与组件。与VC和VB中理解的OBJECT是两个不同的概念。千万不要混淆。一个纯粹封装的OBJCET
它是一个封装好的黑匣子。是一个不能看到内部数据结构的东西。是一个抽象的东西。
最基本的OBJCET,给这个OBJECT 加上接口,用于访问这个黑匣子。那么这个OBJECT就是一个COM OBJECT了。
Func1()
IFOO
Func2()
相当于一个手机充电器。用插销作为标准接口,但是内部实现被隐藏了起来。
在C++中,接口被表示为一个抽象的类。
例如: 程序清单:
class IFoo {
virtual void Func1();
virtual void Func2();
};
特点:
内部有多个纯虚函数。并且没有函数的实现。而且也不能包含任何内部数据成员。
把接口与实现隔离开来的目的:要把对象内部的工作细节隐藏起来,而这些实现都在类中实现。当实现类的数据成员发生变化时,客户程序是与已经编译好的二进制的接口通讯,所以它也无须重新编译。
例子:
手机是一个COM。它的芯片是接口。无论信息如何发生变化。我们都能接受。它的实现是个不不相同。而不同的实现就是不同的国产手机厂家。但是国内所有的厂家生产的手机全部都是使用的国外同一家的芯片产品,也就是说用同一个接口。
常见(有两个接口)
对象支持多接口。如果老版本的接口不方便,可以继承一个新的接口。但不能修改老的接口。接口一旦发布,就不能修改。
如果增加一个接口IFOO2,该接口仍然包含原接口IFOO的两个函数,而且新增加一个函数
IFOO
IFOO2
程序清单:
class IFoo2 : public IFoo {
// Inherited Func1, Func2
virtual void Func2Ex(double nCount) = 0;
};
如何在C++中使用该接口的方法?
实现接口的来首先要从接口继承下来。
实现该接口:
class CFoo : public IFoo {
void Func1() { /* ... */ }
void Func2(int nCount) { /* ... */ }
};
调用:
首先要引入接口的定义。
创建一个指向对象的指针!!这只是为了方便而说的。实际上是错误的。COM中没有对象的指针。只有指向接口的指针。由于VC++调用内部虚拟函数是用了VTBL技术。即建立一个指针表,表中的指针指向成员函数。所以,指向接口的指针实质就是一个指向指针的指针。
将指针指向对象,那么意思实际上就是说,将指针指向对象的接口的简称。
所以,我们可以将指针指向对象的任意一个接口。最后都达到了将指针指向一个对象的目的。
用DELPHI语法写,更清晰易懂。
var pFoo:IFoo;
pFoo:=CFoo.Create;
//实现接口的函数
pFoo.Func1()
COM接口的实现。
COM定义了大量的接口及相对应的IID。
CLSID (class identifier GUID) (identifier:标识)
IID (interface identifier GUID)
GUID:是一个16个字节长的结构。能有3.4的10的38次方的组合。相当与宇宙中原子总数的平方根。所以,永远都用不完的。尽量不要拷贝它。
使用 GUIDGEN.EXE 来生成。
例如 IUnknown
IID of “00000000-0000-0000-c000-000000000046“
任何一个COM对象都包含一个IUNKNOWN接口。所有的接口都是从它这里派生的。
IUnknown 接口有三个函数:
HRESULT QueryInterface(REFIID riid, void **ppvObject);
ULONG AddRef();
ULONG Release();
简要功能介绍:
QueryInterface:用来查询riid对应的接口是否存在。结果在输出参数中。
AddRef:在对象的使用期限内增加引用记数。当对象第一次被创建时或者其他的用户将一个指针指向该对象时,调用该函数,内部记数将增加1。
Release:当用户不再使用的时候,调用它。最后一次调用时,记数变为0,对象将释放自己。
所以,前面的CLASS的定义
class IFoo {
virtual void Func1(void) = 0;
virtual void Func2(int nCount) = 0;
};
应该被改写为:COM对应的定义方式:
interface IFoo : IUnknown {
virtual HRESULT STDMETHODCALLTYPE Func1(void) = 0;
virtual HRESULT STDMETHODCALLTYPE Func2(int nCount) = 0;
};
用宏替换以后:
interface IFoo : IUnknown {
STDMETHOD Func1(void) PURE;
STDMETHOD Func2(int nCount) PURE;
};
创建OBJECT的实例,通过接口的指针引用对象(不能简单的说是:获取指向对象的指针)
IFoo *pFoo = NULL;
HRESULT hr = CoCreateInstance(CLSID_Foo, NULL, CLSCTX_ALL,
IID_IFoo, (void **)&pFoo);
if (SUCCEEDED(hr)) {
pFoo-〉Func1(); // Call methods.
pFoo-〉Func2(5);
pFoo-〉Release(); // MUST release interface when done.
}
一个实际例子:
IUnknown
IFoo
Ifoo2
(IUNKNOWN接口有三个函数)
上边的接口是公共保留接口,左边的是自己定义的接口,都派生自IUNKNOWN。
QueryInterface的作用,获取同一个对象的另外一个接口。
IFoo *pFoo = NULL;
HRESULT hr = CoCreateInstance(CLSID_Foo2, NULL, CLSCTX_ALL,
IID_IFoo, (void **)&pFoo);
if (SUCCEEDED(hr)) {
pFoo-〉Func1(); // call IFoo::Func1
IFoo2 *pFoo2 = NULL;
hr = pFoo-〉QueryInterface(IID_IFoo2, (void **)&pFoo2);
if (SUCCEEDED(hr)) {
int inoutval = 5;
pFoo2-〉Func3(&inoutval); // IFoo2::Func3
pFoo2-〉Release();
}
pFoo-〉Release();
}
IclassFactory的概念:
我们不能让COM对象自己创建自己的实例。要通过服务器。服务器则通过建立一个产生COM对象的工厂:类厂。用IclassFactory来产生很多新的OBJECT。但是,IclassFactory本身也是一个OBJECT。
IDL文件的目的:
达到跨语言实现。它以OSF(Open Software Foundation)的DCE RPC(Distributed Computing Environment Remote Procedure Call) IDL为基础,对它进行了扩展。
[object, uuid(3AB1D289-2145-4a33-9A98-9635C3518CD7)] //uuid和GUID是一个概念。
interface IFoo:Iunkonown
{
HRESULT Func2([in] long * p);
}
在VC中,实际上MFC也支持COM,但是,建议用ATL开发COM。应为它更简单,更纯净,更高级。ATL技术代表着COM技术的精华,提供了建立COM应用的核心构架,大大节省了手工代码量。所以我们从ATL实例开始了解COM技术。
1、定义
(Component Object Model)
COM是微软公司的最高级的,包罗万象的二进制通讯规范(也就是说是大家都要遵守的合同)。用于软件组件间跨进程,跨机器,和操作系统进行交互操作。COM是透明位置的。它可以在EXE,DLL或者远程机器上使用。
OLE是一个主要与用户界面相关的高级功能的集合。COM和OLE的概念界限原本就不清晰,总是容易混淆。
2、历史
OLE(Object Linking & Embedding )是1991年首次出现的(是WINDOWS3.1自带的)。OLE最初的含义是对象链接和嵌入。当时用DDE(动态数据交换)作为底层通讯协议。
1993,COM首次出现。微软推出OLE2.0,开始用COM代替DDE作为底层通讯协议。这也是COM第一个重要的用途。
1996年,大多数开发人员开始编写32位的WIN95应用程序。他们发现,OLE使用COM的方式是一种非常好的设计软件的方法。开发人员开始使用类似的方法编写自己的对象和界面。另外,操作系统也开始要求使用COM技术编程,如编写WIN95用户界面。这些即不是OLE,也不是AUTOMATION,那么他到底是什么呢?这个属于大多数人倾向于使用COM。
3、发展
1996年,微软推出NT4.0,DCOM首次出现,作为NT的一部分。它实现了将COM在分布式系统中的应用。
1997年开始流行ATL。COM作为一种技术规范,最早是由C语言来实现的,但是实现起来比较复杂。出现VC以后,又对COM进行了预制和封装,大大简化COM应用的开发。这就是ATL(Active Template Library)。
4、现状
我们经常见到的用途:
使用外来控件。特别是在网页上使用ACTIVEX控件。ADO
WORD/EXCEL的应用。(两者交叉使用,在应用程序中调用)
二、概念
接口:可以理解为一个抽象的类。
OBJCET(component),相当与组件。与VC和VB中理解的OBJECT是两个不同的概念。千万不要混淆。一个纯粹封装的OBJCET
它是一个封装好的黑匣子。是一个不能看到内部数据结构的东西。是一个抽象的东西。
最基本的OBJCET,给这个OBJECT 加上接口,用于访问这个黑匣子。那么这个OBJECT就是一个COM OBJECT了。
Func1()
IFOO
Func2()
相当于一个手机充电器。用插销作为标准接口,但是内部实现被隐藏了起来。
在C++中,接口被表示为一个抽象的类。
例如: 程序清单:
class IFoo {
virtual void Func1();
virtual void Func2();
};
特点:
内部有多个纯虚函数。并且没有函数的实现。而且也不能包含任何内部数据成员。
把接口与实现隔离开来的目的:要把对象内部的工作细节隐藏起来,而这些实现都在类中实现。当实现类的数据成员发生变化时,客户程序是与已经编译好的二进制的接口通讯,所以它也无须重新编译。
例子:
手机是一个COM。它的芯片是接口。无论信息如何发生变化。我们都能接受。它的实现是个不不相同。而不同的实现就是不同的国产手机厂家。但是国内所有的厂家生产的手机全部都是使用的国外同一家的芯片产品,也就是说用同一个接口。
常见(有两个接口)
对象支持多接口。如果老版本的接口不方便,可以继承一个新的接口。但不能修改老的接口。接口一旦发布,就不能修改。
如果增加一个接口IFOO2,该接口仍然包含原接口IFOO的两个函数,而且新增加一个函数
IFOO
IFOO2
程序清单:
class IFoo2 : public IFoo {
// Inherited Func1, Func2
virtual void Func2Ex(double nCount) = 0;
};
如何在C++中使用该接口的方法?
实现接口的来首先要从接口继承下来。
实现该接口:
class CFoo : public IFoo {
void Func1() { /* ... */ }
void Func2(int nCount) { /* ... */ }
};
调用:
首先要引入接口的定义。
创建一个指向对象的指针!!这只是为了方便而说的。实际上是错误的。COM中没有对象的指针。只有指向接口的指针。由于VC++调用内部虚拟函数是用了VTBL技术。即建立一个指针表,表中的指针指向成员函数。所以,指向接口的指针实质就是一个指向指针的指针。
将指针指向对象,那么意思实际上就是说,将指针指向对象的接口的简称。
所以,我们可以将指针指向对象的任意一个接口。最后都达到了将指针指向一个对象的目的。
用DELPHI语法写,更清晰易懂。
var pFoo:IFoo;
pFoo:=CFoo.Create;
//实现接口的函数
pFoo.Func1()
COM接口的实现。
COM定义了大量的接口及相对应的IID。
CLSID (class identifier GUID) (identifier:标识)
IID (interface identifier GUID)
GUID:是一个16个字节长的结构。能有3.4的10的38次方的组合。相当与宇宙中原子总数的平方根。所以,永远都用不完的。尽量不要拷贝它。
使用 GUIDGEN.EXE 来生成。
例如 IUnknown
IID of “00000000-0000-0000-c000-000000000046“
任何一个COM对象都包含一个IUNKNOWN接口。所有的接口都是从它这里派生的。
IUnknown 接口有三个函数:
HRESULT QueryInterface(REFIID riid, void **ppvObject);
ULONG AddRef();
ULONG Release();
简要功能介绍:
QueryInterface:用来查询riid对应的接口是否存在。结果在输出参数中。
AddRef:在对象的使用期限内增加引用记数。当对象第一次被创建时或者其他的用户将一个指针指向该对象时,调用该函数,内部记数将增加1。
Release:当用户不再使用的时候,调用它。最后一次调用时,记数变为0,对象将释放自己。
所以,前面的CLASS的定义
class IFoo {
virtual void Func1(void) = 0;
virtual void Func2(int nCount) = 0;
};
应该被改写为:COM对应的定义方式:
interface IFoo : IUnknown {
virtual HRESULT STDMETHODCALLTYPE Func1(void) = 0;
virtual HRESULT STDMETHODCALLTYPE Func2(int nCount) = 0;
};
用宏替换以后:
interface IFoo : IUnknown {
STDMETHOD Func1(void) PURE;
STDMETHOD Func2(int nCount) PURE;
};
创建OBJECT的实例,通过接口的指针引用对象(不能简单的说是:获取指向对象的指针)
IFoo *pFoo = NULL;
HRESULT hr = CoCreateInstance(CLSID_Foo, NULL, CLSCTX_ALL,
IID_IFoo, (void **)&pFoo);
if (SUCCEEDED(hr)) {
pFoo-〉Func1(); // Call methods.
pFoo-〉Func2(5);
pFoo-〉Release(); // MUST release interface when done.
}
一个实际例子:
IUnknown
IFoo
Ifoo2
(IUNKNOWN接口有三个函数)
上边的接口是公共保留接口,左边的是自己定义的接口,都派生自IUNKNOWN。
QueryInterface的作用,获取同一个对象的另外一个接口。
IFoo *pFoo = NULL;
HRESULT hr = CoCreateInstance(CLSID_Foo2, NULL, CLSCTX_ALL,
IID_IFoo, (void **)&pFoo);
if (SUCCEEDED(hr)) {
pFoo-〉Func1(); // call IFoo::Func1
IFoo2 *pFoo2 = NULL;
hr = pFoo-〉QueryInterface(IID_IFoo2, (void **)&pFoo2);
if (SUCCEEDED(hr)) {
int inoutval = 5;
pFoo2-〉Func3(&inoutval); // IFoo2::Func3
pFoo2-〉Release();
}
pFoo-〉Release();
}
IclassFactory的概念:
我们不能让COM对象自己创建自己的实例。要通过服务器。服务器则通过建立一个产生COM对象的工厂:类厂。用IclassFactory来产生很多新的OBJECT。但是,IclassFactory本身也是一个OBJECT。
IDL文件的目的:
达到跨语言实现。它以OSF(Open Software Foundation)的DCE RPC(Distributed Computing Environment Remote Procedure Call) IDL为基础,对它进行了扩展。
[object, uuid(3AB1D289-2145-4a33-9A98-9635C3518CD7)] //uuid和GUID是一个概念。
interface IFoo:Iunkonown
{
HRESULT Func2([in] long * p);
}
在VC中,实际上MFC也支持COM,但是,建议用ATL开发COM。应为它更简单,更纯净,更高级。ATL技术代表着COM技术的精华,提供了建立COM应用的核心构架,大大节省了手工代码量。所以我们从ATL实例开始了解COM技术。