• 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来完成组件的定位和创建。
  • 相关阅读:
    终于有了自己的blog了。
    [Asp.Net+C#]Datagrid使用技巧二(怎样让对动态创建的列进行排序)
    [Asp.Net+C#]Datagrid使用技巧一(怎样灵活控制表头)
    CentOS下配置iptables防火墙
    ios中提示信息的实现及自动消失
    ios导航条添加按钮
    NSAutoreleasePool自动释放池
    什么是Tollfree bridging
    Android开发中的drawable(hdpi,mdpi,ldpi)和WVGA,HVGA,QVGA的区别以及联系
    文章逐步迁移过来
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/13560661.html
Copyright © 2020-2023  润新知