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來完成組件的定位和創建。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM