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