COM技術入門(1)

一、COM簡介1

1.COM是微軟組件對象模型檢測,由於COM具有二進制代碼共享特性,所以它具備

高可開發性,高度可維護性,和高度可移植性,以至於Winows很多軟件都採用COM

做整體架構,比如微軟DirectX等,COM流行於2000-2004,由於它的普及面廣,

應用繁多,加上Windows對齊默認支持,開發出的軟件無需依賴其他開發包.

所以被很多開發公司採用, 坐位一個VC++程序員,是否掌握COM用法

稱爲是否合格的表格指標.

2.COM是一門深邃的思想和技術.要想完全掌握的話,沒有半年是很難做到的.

3.優點:採用COM組件架構我們的軟件,可以使已編寫好的功能模塊可以

很方便的移植到其他平臺比如C++的MFC平臺移植到C#的WinForm平臺

因爲COM組件是誇應用的,可以被C++調用,也可以被C#調用



二、COM接口與QueryInterface

1.現實中的組件和接口

a) 一個手機,一個MP3,一個充電寶,那麼充電寶的作用就是給手機充電,

充電寶上有一個接口的USB的,有一根數據線連接到手機上,給手機充電

b) 手機充完電後, 把線接到MP3上,這時候就給MP3充電了.

c) 這3個物件中:手機本身是一個組件,MP3也是一個組件,充電寶也是組件。

因爲他們都具備可獨立性,

組件和組件通過什麼連接呢? 就是通過一個公用的USB接口.進行溝通.

d) 接口的定義:接口是組件和組件之間進行交換的,

CPU內存條這些有固定的接口,因此你更換CPU的時候也必須要相同接口的CPU.

主板不直接識別CPU,而是通過那個接口來識別CPU。

e) 接口的協議確定好後往往是不能再改變了,比如五號電池不能做的大,也不能小.



2.C++程序中的組件與接口

定義接口:

#define interface struct

在c++中interface其實就是struct

定義兩個接口

interface OPEN

{

    virtual void OPENOK()=0;

    virtual voud OPENERROR()=0;

}

interface CLOSE

{

    virtual void CLOSEOK()=0;

    virtual voud CLOSEERROR()=0;

}



定義組件 組件是派生與接口的,

class BES : public OPEN,public CLOSE

{

public:

    //實現接口....

};

BES* b = new BES();//組件實例

OPEN* t = b;//獲取接口

t->OPENOK();//調用接口

//組件第二個接口

CLOSE* c = b;//獲取接口

t->CLOSEOK();//調用接口

組件可以有多個接口, 這些接口又有不同的功能

銷燬組件 delete



3.COM組件與接口

a) COM組件通過二進制代碼共享實現跨應用,比如應用A的COM組件

的可以直接把這個COM組件拿到應用B來用(開發)

b)class類文件源碼這些就是普通的源碼共享,COM是二進制共享方式.

c) COM組件完全與變成語言無關的,可以被VC調用,可被網頁調用,可被.net調用

d) COM組件只能運行在Windows平臺,不能再Linux和蘋果機上面運行.

e) 

IUnknow接口 聲明

interface IUnknown

{

    virtual HRESULT QueryInterface(const IID&iid,void** ppv)=0;

     virtual ULONG AddRef()=0;

     virtual ULONG Release()=0;

}




5.QueryInterface函數,與HRESULT類型,IID類型,數據類型轉換

----- QueryInterface函數 

a) 查詢某個組件是否支持特定的接口,比如說上面的BES組件他有兩個接口

我想要知道BES組件是否支持其他的接口,我在外部是不知道你支持還是

不支持的,所有通過QueryInterface來查詢接口知道你是否支持這個接口.

b) 返回結果是通過第二個參數輸出的,你把組件指針傳進去,不爲NULL就是找到接口.

c)返回值HRESULT

S_OK 成功

S_FALSE 函數成功執行完成,但返回出現錯位

E_INVALIDARG 參數有錯誤

E_OUTOFMEMORY 內存申請粗我

E_UNEXPECTED 未知異常

E_NOTIMPL 未實現的功能

E_FAIT 沒有詳細說明的粗我

E_POINTER 無效的指針

E_HANDLE 無效的句柄

E_ABOUT 終止操作

E_ACCESSDENIED 訪問被拒絕

E_NOINTERFACE 不支持接口



-----IID

IID是接口表示符,Interface ID ,每個接口都可以設置一個接口標識符

用來標誌這個接口,標誌了某個接口之後IID的值是不能再修改的,

GUID定義

typedef struct _GUID

{

    DWORD D1;//隨機數

    WORD D2; //和時間有關

    WORD D3; //和時間有關

    BYTE D4[8]; //和網關MAC相關

}GUID

GUID生成方法 在VS工具裏有一個創建GUID


blob.png

理論上將GUID是不可能重複的,重複可能性非常小,每秒鐘一萬億個GUID的情況下

即使太陽變成白矮星,他也是唯一的.

("40125064-D4C6-4165-8535-72D35FE44D21")

GUID這樣定義的 用逗號分隔

static const GUID guid = 
{

0x40125064,

0xD4C6,

0x4165,

{

    0x85,

    0x35,

    0x72,

    0xD3,

    0x5F,

    0xe4,

    0x4d,

    0x21   

}

}


在編輯器裏這個就表示一個接口標識符,它是固定的.

注意我說的不是圖片裏的固定, 

而是你自己去生成的, 我這個只是一個示範.

blob.png

blob.png




6.接口繼承自IUnknown

然後組件實現IUnknown接口裏的函數

blob.png


QueryInterface實現

	virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,void**ppv)
	{
		//ppv返回的接口指針 0就是沒有找到該接口
		if(iid == IID_IUnknown)
		{
			//即使CPD 繼承兩個IUnknown接口,其中一個是OPEN
			//另一個是CLOSE 
			//直接防護當前這個指針
			*ppv = (IID_OPENS*)this;
		}
		else if(iid == IID_OPEN_KEY) //對比唯一的GUID接口標識符
		{
			*ppv = (IID_OPENS*)this;
		}
		else if(iid == IID_CLOSE_KEY)
		{
			*ppv = (IID_CLOSE*)this;
		}
		else
		{
			//查詢不到IID *ppv返回NULL
			*ppv = NULL;
			//返回E_NOINTERFACE 表示組件沒有該接口(不支持這個接口)
			return E_NOINTERFACE; 
		}

		return S_OK;
	}


組件調用該函數,查詢這個組件是否支持該接口

	//實例化組件
	CPD* p = new CPD();

	IUnknown* pkonw = NULL;
	HRESULT hr;
	//從組件查詢UInknow組件
	hr = p->QueryInterface(IID_IUnknown,(void**)&pkonw);

	if(SUCCEEDED(hr)) //對HRESULT返回值判斷 一般用SUCCEEDED
	{
		//然後從IUnknown查詢 OPEN接口
		IID_OPENS* op  = NULL;
		hr = pkonw->QueryInterface(IID_OPEN_KEY,(void**)&op);
		if(SUCCEEDED(hr)) 
		{
			//調用接口的方法
			op->Open();
		}

	}

	delete p;


判斷兩個接口是否相同

if((void*)接口指針1 != (void*)接口指針2)

{

    兩個接口不同

}



blob.png

blob.png




三、引用計數 AddRef和Release


1.內存資源何時釋放

組件 = nwe 組件

然後delete 組件,這樣就銷燬一個組件了

但是把接口傳給組件成員變量

組件 = new 組件(接口指針變量)

怎麼才能知道什麼時候不用這個組件

在main主函數最後面執行delete組件,銷燬???


主函數最後面銷燬組件實例,可行,但不是一個好辦法

因爲這樣最終是釋放組件實例的內存資源,不過卻不是

即時(在實例所指組件不用時)地釋放內存資源,

這個程序在運行中,佔內存將是巨大的

解決這個問題需要用到引用計數技術.


2.引用計數原理

a) 引用計數就是用啦管理對象聲明週期的一種技術

b) 對象O可能被外界A,外界B,外界C引用

也就是說A,B,C都在使用這個對象O

c) 每次外界對象引用這個對象,計數器就+1

每次外界不用對象時,計數器減1

d) 在計數值爲0時,執行delete this銷燬自己的資源.

e) 引用計數使得對象通過引用計數能夠知道何時對象

不再被使用,然後及時地刪除自身所佔的內存資源

f) IUnknown接口的AddRef與Release就是引用計數實現方式



3.AddRef與Release實現與使用

類裏有一int類型的變量在構造函數裏初始化爲0;

他就是引用計數器,然後構造函數調用AddRef引用計數+1

virtual ULONG STDMETHODCALLTYPE AddRef(){return m_Icount++; }

Release實現

virtual ULONG STDMETHODCALLTYPE Release()

{

if(--m_Icount == 0)

{

delete this;

return 0;

}

return m_Icount;

};



如果接口不再調用必須手動調用Release

再賦值後也要調用AddRef()











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章