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来完成组件的定位和创建。
posted @ 2020-08-25 17:02  卖雨伞的小男孩  阅读(1536)  评论(0编辑  收藏  举报