COM的IID、CLSID、IDL
一、UUID
通过特定的算法将主机和时间印鉴结合起来得到的数值;
组件对UUID的使用得到的叫做GUID;
如果我们用GUID唯一的表示组件的类,又叫做CLSID
如果我们用GUID唯一的表示组件的接口,又叫做IID
…(typedef)
GUID的产生方法:
1.GUIDGEN.exe
2.HRESULT CoCreateGuid (GUID * pguid
二、IDL
是组件的核心部分,用来描述组件接口的语言,定义COM接口。
作用:
剥离了编程语言和平台的限制,促进了建立二进制的组件模型。
COM IDL = = RPC IDL + 继承、多态等性质
IDL由微软的MIDL编译器进行编译
三、接口与IUnknown
import "unknwn.idl"; //作用类似include,表示引入IDL的文档
[
object,//指定该接口是一个COM接口,不是RPC接口
uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx), //UUID唯一的标识
]
//[]中内容的表示属性
interface Imath : IUnknown //关键字interface白表示定义接口,接口继承自IUnkonw接口
{ //
HRESULT Add([in] long op1,[in] long op2,[out,retval] long * pval);
}
MIDL将上面代码映射成C++即为:
#include"unknwn.h"
class Imath:public IUnknown
{
public:
virtual BOOL _stdcall Add(long op1,long op2,long *p) = 0;
}
以上代码讲述的是IDL的基本使用。
接下来通过实现带有两个接口(ISimpleMath和IAdvancedMath)的Math组件来说明
接口的查询:
1.每个组件的都需要实现一个IUnknown接口,所以Math组件也需要,给其他的接口继承
class IUnknown
{
public:
virtual HRESULT _stacall QueryInterface([in] REFIIID iid , [out] void ** ppv) = 0;
// iid是客户要查询的接口id,如果有这个接口,ppv即为返回给客户的接口指针
virtual ULONG _stdcall Addref() = 0;
//增加对象的引用计数
virtual ULONG _stdcall Release() = 0;
//减少对象的引用计数
}
2.要想将接口给客户使用,每个接口都需要从IUnknown接口继承,然后客户才能去查询,得到接口指针使用,这里接口主要包括ISimpleMath和IAdvancedMath
2.1 Math的第一个接口,ISimpleMath:
class ISimpleMath:public IUnknown //直接继承IUnknown接口
{
public:
//返回值告诉客户这个接口到底有没有,函数最终的结果通过调用函数前定义好的量保存 *re
virtual HRESULT Add(long op1,long op2,long *re) = 0; //加
virtual HRESULT Sub(long op1,long op2,long *re) = 0; //减
virtual HRESULT Mul(long op1,long op2,long *re) = 0; //乘
virtual HRESULT Divi(long op1,long op2,long *re) = 0; //除
}
2.2 Math的第二个接口,IAdvancedMath:
class IAdvancedMath:public IUnknown //直接 继承IUnknown接口
{
public:
//返回值告诉客户这个接口到底有没有,函数最终的结果通过调用函数前定义好的量保存 *p
virtual HRESULT fac(short op,long *p) = 0;
virtual HRESULT fib(short op ,long *p) = 0;
}
3.Math接口又要继承自ISimpleMath和IAdvancedMath接口:
class Math: public ISimpleMath,public IAdvancedMath //间接的基础了IUnknown接口
{
public:
Math(); ~Math(); //构造和析构函数
//IUnknownD的 三个成员函数放在这里实现,再次声明以下
virtual HRESULT _stacall QueryInterface(const IID& iid , void ** ppv) ;
virtual ULONG _stdcall Addref() ;
virtual ULONG _stdcall Release() ;
//为了直观的说明问题,我在这里分别只实现ISimpleMath和IAdvancedMath的一个成员函数
virtual void Add(long op1,long op2,long *re) //ISimpleMath
{
*re = op1 + op2;
cout<<op1<<"+"<<op2<<" ="<<*re<<endl;
}
virtual void fac(short op,long *p)
{
//具体实现省略
}
private:
int m_ref; //对象的引用计数值 ############(重要)
}
//类外实现
Math::Math()
{
m_ref = 0;
}
//接口查询的实现 (重要)
HRESULT Math::QueryInterface(const IID&iid ,void ** ppv)
{
if (iid == IID_IUnknown)
{
*ppv = static_cast<ISimpleMath*>(this);
((ISimpleMath*)(*ppv) ) -> Addref();
}
else if (iid == IID_ISimpleMath)
{
*ppv = static_cast<ISimpleMath*>(this);
((ISimpleMath*)(*ppv) ) -> Addref();
}
else if (iid == IID_IAdvancedMath)
//如果iid是IAdvancedMath的id,那么就将*ppv内容改成IAdvancedMath的接口指针
{
*ppv = static_cast<IAdvancedMath*>(this);
((IAdvancedMath*)(*ppv) ) -> Addref();
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
//增加引用计数
ULONG Math::AddRef()
{
m_ref ++;
return (ULONG )m_ref;
}
//减少引用计数
ULONG Math::Release()
{
m_ref --;
if(m_ref == 0)
{
delete this; //减到0 删除自身
return 0;
}
return (ULONG)m_ref;
}
COM规范给的接口的特性:
1.IUnknown接口的唯一性;
2.接口的对称性;
3.接口的传递性;
4.接口的自反性;
5.接口查询时间无关性
四、COM对象
1.注册表:
是windows操作系统的中心数据仓库,当组件程序安装在机器上后,必须要把它的信息记录到注册表中,客户才能从表中找到组件程序进行操作。
注册表是一个树状的层次结构,根节点下包含:层层递进
键(key)和值(value)
子键和值
注册表是客户与组件之间的中介,是COM实现位置透明性的关键。
COM只使用了注册表的一个分支:HKEY_CLASSES_ROOT,在此键下可以看到CLSID键
HKEY_CLASSES_ROOT包含的内容:
1.文件扩展名:已经注册过的文件的扩展名
2.ProgID:字符串化的组件名字(program IDentifier)
3.AppID:在此键下的子键的作用是将某个AppID映射成某个远程服务器名称,分布式COM(DCOM)用到
4.CLSID:COM对象的唯一标识符
5.Interface:主要用于跨进程,将IID映射成与某个接口相关的信息
6.TypeLib:可以将一个LIB映射成存储类型库的文件名。
windows下,所以组件必须先注册后使用,组件模块都自带注册信息。
进程内组件的注册过程:DLL
由于进程内的组件是DLL,本身是不能执行的,需要某个进程调用才能获得控制
注册:RegSvr32.exe c:\MyMathDll.dll
注销:RegSvr32.exe \u c:\MyMathDll.dll
进程外组件的注册过程:EXE
进程外的组件本身是个EXE,可以执行,所以执行过程中完成自身的注册工作。
注册命令:myExeCom /RegServer
注销命令:myExeCom /UnRegServer
五、COM库
COM库的作用:
充当组件程序和客户程序之间的桥梁,尤其是组件对象的创建过程中,以及对象管理、内存管理和一些标准化的操作
COM库的几个重要操作:
1.COM库的初始化:使用COM库之前需要初始化
HRESULT CoIntialize(Imalloc * pMalloc);
2.内存管理:
COM是建立在二进制的基础上,所以并不使用new之类的与语言有关的管理内存,它是COM库统一管理的
class IMalloc:public IUnknown
{
void * Alloc(ULONG cb) = 0;//分配
void *Realloc(void *pv,ULONG cb) = 0;//重新分配内存
void Free(void * pv) = 0;//
// ... ...
}
3.创建COM对象:
COM中的对象的创建并不是C++中的new,它是建立在二进制的基础上,所以由COM库统一接口
函数定义如下:
Exern “C” _stdcall HRESULT CoCreateInstance
(
REFCLSID rclsid, //将要创建对象的ID
LPUNKNOWN pUnkOter, //用于被聚合的情形
DWORD dwClsContext, //指定组件的类别,进程内或者进程外
REFIIF riid, //COM接口的ID比如IID_ISimpleMath
void ** ppv //用来保存COM接口指针
)
在使用CoCreateInstance时,客户只需知道创建组件的CLSID和要查询接口的IID,
而不用关系组件实现代码位于何处,COM来完成组件的定位和创建。