COM技术
COM(Component Object Model,组件对象模型),是由微软推出的一套接口规范,通过设定不同组件之间需要遵守的标准与协议,主要用来跨语言、跨进程之间的模块通信。
中文名 COM技术 外文名 Component Object Model 正 文 首先,用户一般希望能够定 正文2 COM+是由微软推出的
目录
1 简介
2 优点
简介
所谓COM(Component Object Model,组件对象模型)是一种说明如何建立可动态互变组件的规范,此规范提供了为保证能够互操作,客户和组件应遵循的一些二进制和网络标准。通过这种标准将可以在任意两个组件之间进行通信而不用考虑其所处的操作环境是否相同、使用的开发语言是否一致以及是否运行于同一台计算机。
优点
COM的优点?
首先,用户一般希望能够定制所用的应用程序,而组件技术从本质上讲就是可被定制的,因而用户可以用更能满足他们需要的某个组件来替换原来的那个。其次,由于组件是相对应用程序独立的部件,我们可以在不同的程序中使用同一个组件而不会产生任何问题,软件的可重用性将大大的得到增强。第三,随着网络带宽及其重要性的提高,分布式网络应用程序毫无疑问的成为软件市场上越来越重要的买点。组件架构可以使得开发这类应用程序的过程得以简化。
什么是COM+?
COM+是由微软推出的,基于COM进行再一次优化。
COM+并不是COM的简单升级,COM+的底层结构仍然以COM为基础,它几乎包容了COM的所有内容,COM+综合了COM、DCOM和MTS这些技术要素,它把COM组件软件提升到应用层而不再是底层的软件结构,它通过操作系统的各种支持,使组件对象模型建立在应用层上,把所有组件的底层细节留给操作系统,因此,COM+与操作系统的结合更加紧密。
COM+不再局限于COM的组件技术,它更加注重于分布式网络应用的设计和实现。COM+继承了COM几乎全部的优势,同时又避免了COM实现方面的一些不足,把COM、DCOM和MTS的编程模型结合起来,继承了它们的绝大多数特性,在原有的特性上增加了新的功能。
COM+的新的优点?
以下列出COM+的几个主要特性:
COM+不仅继承了COM所有的优点,而且还增加了一些服务,比如队列服务、负载平衡、内存数据库、事件服务等。
队列服务对于分布式应用非常有意义,特别是在现在网络速度很慢的情况下,这种机制可以保证应用系统能够可靠地运行。在应用系统包含大量节点但服务器又繁忙的情况下,客户应用程序可以把它们的请求放到队列中,当服务器负载比较轻的时候再处理这些请求;
又如COM+提供了负载平衡服务,它可以实现动态负载平衡,而且COM+应用程序的负载平衡特性并不需要编写代码来支持,客户程序和组件程序都可以按通常的方式实现。获得负载平衡特性并不是用程序设计的方式来实现的,而是通过配置实现分布式应用程序的负载平衡,如上所讲的队列服务,其实也反映了一种负载平衡。
(1) 真正的异步通讯。COM+底层提供了队列组件服务,这使客户和组件有可能在不同的时间点上协同工作,COM+应用无须增加代码就可以获得这样的特性。
(2) 事件服务。新的事件机制使事件源和事件接收方实现事件功能更加灵活,利用系统服务简化了事件模型,避免了COM可连接对象机制的琐碎细节。
(3) 可伸缩性。COM+的可伸缩性来源于多个方面,动态负载平衡以及内存数据库、对象池等系统服务都为COM+的可伸缩性提供了技术基础,COM+的可伸缩性原理上与多层结构的可伸缩特性一致。
(4) 可管理和可配置性。管理和配置是应用系统开发完成后的行为,在软件维护成本不断增加的今天,COM+应用将有助于软件厂商和用户减少这方面的投入。
(5) 易于开发。COM+应用开发的复杂性和难易程度将决定COM+的成功与否,虽然COM+开发模型比以前的COM组件开发更为简化,但真正提高开发效率仍需要借助于一些优秀的开发工具。
COM+标志着Microsoft的组件技术达到了一个新的高度,它不再局限于一台机器上的桌面系统,它把目标指向了更为广阔的企业内部网,甚至Internet国际互连网络。COM+与多层结构模型以及Windows操作系统为企业应用或Web应用提供了一套完整的解决方案。
========
COM技术初探(一)
http://blog.csdn.net/yongjunhe/article/details/8588208目录
一. COM是一个更好的 C++
1. COM 是什么
2. 从 C++ 到 DLL 再到 COM
2.1 C++
2.2 DLL
2.3 COM
二. COM基础
1. COM基本知识
1.1 返回值HRESULT
1.2 初识idl
1.3 IUnkown接口
2. 一个比较简单的COM
2.1 interface.h文件
2.2 math.h文件
2.3 math.cpp文件
2.4 simple.cpp文件
2.5 Math组件的二进制结构图
2.6 小结
三. 纯手工创建一个COM组件
1. 从建工程到实现注册
1.1 创建一个类型为win32 dll工程
1.2 定义接口文件
1.3 增加注册功能
1.3.1 增加一个MathCOM.def文件
1.3.2 DllRegisterServer()和DllUnregisterServer()
1.4 MathCOM.cpp文件
1.5 小结
2. 实现ISmipleMath,IAdvancedMath接口和DllGetClassObject()
2.1 实现ISmipleMath和IAdvancedMath接口
2.2 COM组件调入大致过程
2.3 DllGetClassObject()实现
2.4 客户端
2.5 小结
3. 类厂
附录
A 我对dll的一点认识
一. 没有lib的dll
1.1 建一个没有lib的dll
1.2 调试没有lib的dll
二. 带有lib的dll
2.1 创建一个带有lib的dll
2.2 调试带有引用但没有头文件的dll
三. 带有头文件的dll
3.1 创建一个带有引出信息头文件的dll
3.2 调试带有头文件的dll
四. 小结一、COM是一个更好的C++
1、COM 是什么
Don Box 说"COM IS LOVE"。COM 的全称是 Component Object Model 组件对象模型。
2、从 C++ 到 DLL 再到 COM
2.1 C++
如某一软件厂商发布一个类库(CMath四则运算),此时类库的可执行代码将成为客户应用中不可分割的一部分。假设此类库的所产生的机器码在目标可执行文件中占有4MB的空间。当三个应用程序都使用CMath库时,那么每个可执行文件都包含4MB的类库代码(见图1.1)。当三个应用程序共同运行时,他们将会占用12MB的虚拟内存。问题还远不于此。一旦类库厂商发现CMath类库有一个缺陷后,发布一个新的类库,此时需要要求所有运用此类库的应用程序。此外别无他法了。
图1.1 CMath 的三个客户
2.2 DLL
解决上面问题的一个技术是将CMath类做成动态链接库(DLL ,Dynamic Link Library)的形式封装起来 。
在使用这项技术的时候,CMath的所有方法都将被加到 CMath dll 的引出表(export list)中,而且链接器将会产生一个引入库(import library)。这个库暴露了CMath的方法成员的符号 。当客户链接引入库时,有一些存根会被引入到可执行文件中,它在运行时通知装载器动态装载 CMath Dll。
当 CMath 位于dll中时,他的运行模型见图1.2
图1.2 CMath引入库
2.3 COM
"简单地把C++类定义从dll中引出来"这种方案并不能提供合理的二进制组件结构。因为C++类那既是接口也是实现。这里需要把接口从实现中分离出来才能提供二进制组件结构。此时需要有二个C++类,一个作为接口类另一个作为实现类。让我们开始COM之旅吧。
二、COM基础
1、 COM基本知识
1.1 返回值HRESULT
COM要求所有的方法都会返回一个HRESULT类型的错误号。HRESULT 其实就一个类型定义:
1.
typedef LONG HRESULT;
有关HRESULT的定义见 winerror.h 文件
01.
// Values are 32 bit values layed out as follows:
02.
//
03.
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
04.
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
05.
// +-+----+-------------------------+---------------------------------+
06.
// |S| Res| Facility | Code |
07.
// +-+----+-------------------------+---------------------------------+
08.
//
09.
// where
10.
//
11.
// S - is the severity code
12.
//
13.
// 0 - Success
14.
// 1 - Error
15.
//
16.
// Res- is a reserved bit
17.
//
18.
// Facility - is the facility code
19.
//
20.
// Code - is the facility''s status code
我们一般下面的宏来判断方法是否成功:
1.
#define SUCCEEDED(hr)(long(hr)>=0)
2.
#define FAILED(hr)(long(hr)<0)
1.2 初识 IDL
每个标准的COM组件都需要一个接口定义文件,文件的扩展名为IDL。让我们看IUnknow接口的定义文件是怎样的。
01.
[
02.
local,
03.
object,
04.
uuid(00000000-0000-0000-C000-000000000046),
05.
pointer_default(unique)
06.
]
07.
08.
interface IUnknown
09.
{
10.
typedef [unique] IUnknown *LPUNKNOWN;
11.
12.
cpp_quote("")
13.
cpp_quote("// IID_IUnknown and all other system IIDs are provided in UUID.LIB")
14.
cpp_quote("// Link that library in with your proxies, clients and servers")
15.
cpp_quote("")
16.
17.
HRESULT QueryInterface(
18.
[in] REFIID riid,
19.
[out, iid_is(riid)] void **ppvObject);
20.
ULONG AddRef();
21.
ULONG Release();
22.
}
23.
24.
[local]属性禁止产生网络代码。
25.
[object]属性是表明定义的是一个COM接口,而不是DEC风格的接口。
26.
[uuid]属性给接口一个GUID。
27.
[unique]属性表明null(空)指针为一个合法的参数值。
28.
[pointer_defaul]属性所有的内嵌指针指定一个默认指针属性
29.
typedef [unique] IUnknown *LPUNKNOWN;这是一个类型定义
30.
cpp_quote这个比较有趣,这是一个在idl文件写注解的方法。这些注解将保存到***.h和***_i.c文件中
31.
[in]表示这个参数是入参
32.
[out]表示这个参数是出参
33.
[iid_is(riid)]表示这个参数需要前一个的riid 参数。
注意:所有具有out属性的参数都需要是指针类型。
1.3 IUnkown接口
在整个例子除了IUnkown这个东西,其他应该不会感到陌生吧!COM要求(最基本的要求)所有的接口都需要从IUnknown接口直接或间接继承,所以IUnknown接口有"万恶之源"之称。
IUnkown接口定义了三个方法。
1.
HRESULT QueryInterface([in] REFIID riid,[out] void **ppv);
2.
ULONG AddRef();
3.
ULONG Release();
其中 AddReft() 和Release()负责对象引用计数用的,而 QueryInterface()方法是用于查询所实现接口用的。每当COM组件被引用一次就应调用一次AddRef()方法。而当客户端在释放COM组件的某个接口时就需要调用Release()方法。
这里所讲的请在下面的例子仔细体会。
2、一个比较简单的COM
此例子共有四个文件组成:
文件名 说明
Interface.h 接口类定义文件
Math.h和Math.cpp 实现类文件
Simple.cpp 主函数文件 这里用来当作COM的客户端
2.1 interface.h 文件
01.
#ifndef INTERFACE_H
02.
#define INTERFACE_H
03.
#include
04.
05.
//{7C8027EA-A4ED-467c-B17E-1B51CE74AF57}
06.
static const GUID IID_ISimpleMath =
07.
{ 0x7c8027ea, 0xa4ed, 0x467c, { 0xb1, 0x7e, 0x1b, 0x51, 0xce, 0x74, 0xaf, 0x57 } };
08.
09.
//{CA3B37EA-E44A-49b8-9729-6E9222CAE84F}
10.
static const GUID IID_IAdvancedMath =
11.
{ 0xca3b37ea, 0xe44a, 0x49b8, { 0x97, 0x29, 0x6e, 0x92, 0x22, 0xca, 0xe8, 0x4f } };
12.
13.
interface ISimpleMath : public IUnknown
14.
{
15.
public:
16.
virtual int Add(int nOp1, int nOp2) = 0;
17.
virtual int Subtract(int nOp1, int nOp2) = 0;
18.
virtual int Multiply(int nOp1, int nOp2) = 0;
19.
virtual int Divide(int nOp1, int nOp2) = 0;
20.
};
21.
22.
interface IAdvancedMath : public IUnknown
23.
{
24.
public:
25.
virtual int Factorial(int nOp1) = 0;
26.
virtual int Fabonacci(int nOp1) = 0;
27.
};
28.
#endif
此文件首先 #include 将 IUnknown 接口定义文件包括进来。
接下来定义了两个接口,GUID(Globally Unique Identifier全局唯一标识符)它能保证时间及空间上的唯一。
ISmipleMath接口里定义了四个方法,而IAdvancedMath接口里定义了二个方法。这些方法都是虚函数,而整个 ISmipleMath 与 IAdvancedMath 抽象类就作为二进制的接口。
2.2 math.h文件
01.
#include "interface.h"
02.
03.
class CMath : public ISimpleMath,
04.
public IAdvancedMath
05.
{
06.
private:
07.
ULONG m_cRef;
08.
09.
private:
10.
int calcFactorial(int nOp);
11.
int calcFabonacci(int nOp);
12.
13.
public:
14.
//IUnknown Method
15.
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
16.
STDMETHOD_(ULONG, AddRef)();
17.
STDMETHOD_(ULONG, Release)();
18.
19.
// ISimpleMath Method
20.
int Add(int nOp1, int nOp2);
21.
int Subtract(int nOp1, int nOp2);
22.
int Multiply(int nOp1, int nOp2);
23.
int Divide(int nOp1, int nOp2);
24.
25.
// IAdvancedMath Method
26.
int Factorial(int nOp);
27.
int Fabonacci(int nOp);
28.
};
此类为实现类,他实现了ISmipleMath和IAdvancedMath两个接口类(当然也可以只实现一个接口类)。
请注意:m_cRef 是用来对象计数用的。当 m_cRef 为0组件对象应该自动删除。
2.3 math.cpp文件
01.
#include "interface.h"
02.
#include "math.h"
03.
04.
STDMETHODIMP CMath::QueryInterface(REFIID riid, void **ppv)
05.
{// 这里这是实现dynamic_cast的功能,但由于dynamic_cast与编译器相关。
06.
if(riid == IID_ISimpleMath)
07.
*ppv = static_cast(this);
08.
else if(riid == IID_IAdvancedMath)
09.
*ppv = static_cast(this);
10.
else if(riid == IID_IUnknown)
11.
*ppv = static_cast(this);
12.
else {
13.
*ppv = 0;
14.
return E_NOINTERFACE;
15.
}
16.
17.
reinterpret_cast(*ppv)->AddRef(); //这里要这样是因为引用计数是针对组件的
18.
return S_OK;
19.
}
20.
21.
STDMETHODIMP_(ULONG) CMath::AddRef()
22.
{
23.
return ++m_cRef;
24.
}
25.
26.
STDMETHODIMP_(ULONG) CMath::Release()
27.
{
28.
ULONG res = --m_cRef; // 使用临时变量把修改后的引用计数值缓存起来
29.
if(res == 0) // 因为在对象已经销毁后再引用这个对象的数据将是非法的
30.
delete this;
31.
return res;
32.
}
33.
34.
int CMath::Add(int nOp1, int nOp2)
35.
{
36.
return nOp1+nOp2;
37.
}
38.
39.
int CMath::Subtract(int nOp1, int nOp2)
40.
{
41.
return nOp1 - nOp2;
42.
}
43.
44.
int CMath::Multiply(int nOp1, int nOp2)
45.
{
46.
return nOp1 * nOp2;
47.
}
48.
49.
int CMath::Divide(int nOp1, int nOp2)
50.
{
51.
return nOp1 / nOp2;
52.
}
53.
54.
int CMath::calcFactorial(int nOp)
55.
{
56.
if(nOp <= 1)
57.
return 1;
58.
59.
return nOp * calcFactorial(nOp - 1);
60.
}
61.
62.
int CMath::Factorial(int nOp)
63.
{
64.
return calcFactorial(nOp);
65.
}
66.
67.
int CMath::calcFabonacci(int nOp)
68.
{
69.
if(nOp <= 1)
70.
return 1;
71.
72.
return calcFabonacci(nOp - 1) + calcFabonacci(nOp - 2);
73.
}
74.
75.
int CMath::Fabonacci(int nOp)
76.
{
77.
return calcFabonacci(nOp);
78.
}
79.
CMath::CMath()
80.
{
81.
m_cRef=0;
82.
}
此文件是CMath类定义文件。
2.4 simple.cpp文件
01.
#include "math.h"
02.
#include
03.
04.
using namespace std;
05.
06.
int main(int argc, char* argv[])
07.
{
08.
ISimpleMath *pSimpleMath = NULL;//声明接口指针
09.
IAdvancedMath *pAdvMath = NULL;
10.
11.
//创建对象实例,我们暂时这样创建对象实例,COM有创建对象实例的机制
12.
CMath *pMath = new CMath;
13.
14.
//查询对象实现的接口ISimpleMath
15.
pMath->QueryInterface(IID_ISimpleMath, (void **)&pSimpleMath);
16.
if(pSimpleMath)
17.
cout << "10 + 4 = " << pSimpleMath->Add(10, 4) << endl;
18.
19.
//查询对象实现的接口IAdvancedMath
20.
pSimpleMath->QueryInterface(IID_IAdvancedMath, (void**)&pAdvMath);
21.
if(pAdvMath)
22.
cout << "10 Fabonacci is " << pAdvMath->Fabonacci(10) << endl;
23.
24.
pAdvMath->Release();
25.
pSimpleMath->Release();
26.
return 0;
27.
}
此文件相当于客户端的代码,首先创建一个CMath对象,再根据此对象去查询所需要的接口,如果正确得到所需接口指针,再调用接口的方法,最后再将接口的释放掉。
2.5 Math组件的二进制结构
2.6 小结
此例子从严格意义上来并不是真正的COM组件(他不是dll),但他已符合COM的最小要求(实现IUnknown接口)。接下来我们来做一COM dll(但还不用ATL)。
========
资源链接
http://blog.csdn.net/libin_1/article/details/45271443
COM技术内幕笔记
http://blog.csdn.net/byxdaz/article/details/6595210
COM编程小结
http://blog.csdn.net/zang141588761/article/category/6202247
COM编程精彩实例